Hacker News new | past | comments | ask | show | jobs | submit login

As has been noted in other forums, this article does not cover implementing Functor/Monad for the Option type family (for example) faithfully.

What's missing is implementing Functor for `Option` (the "unapplied" option, without a type parameter), in such a way that it is evident in from the trait implementation that if you pass an Option to `fmap`, then you get an Option out (not just any Functor). In other words, that the particular flavour of functor or monad is preserved in these operations.

Proof of concept code for this exists, but Rust doesn't support it in a fluent way, even with the GAT feature.




> What's missing is implementing Functor for `Option` (the "unapplied" option, without a type parameter), in such a way that it is evident in from the trait implementation that if you pass an Option to `fmap`, then you get an Option out (not just any Functor).

I was momentarily confused by this explanation, so allow me to distill the problem.

In the trait declaration for Functor, nothing requires that `<X<T> as Functor>::Wrapped<T> == X<T>`. In other words, the implementing type can be different from the return type of `map`. You would want to return `Self<T>` from `map`, but `Self` refers to the fully-reified type, which is to say the implementor has already decided what the type parameter (if any) is, and you as the trait author have no control over it.

You need some way to force the above equality, which is probably what the referenced "proof of concept" does. (I think I found it here: https://github.com/edmundsmith/type-plugs)

(EDIT: I found another proof of concept via the author's bug report. The two approaches seem pretty much the same, and it doesn't look too difficult to understand. https://users.rust-lang.org/t/monads-in-rust-with-gats/50487)

The article does mention this problem explicitly:

>> Interestingly, we have lost the knowledge here that Self::Wrapped<T> is also a Pointed. That's going to be a recurring theme for the next few traits.


Adding that restriction would make it impossible to implement Functor for lazy collections (for example, `<I as Iterator>::map()` returns a `Map<I, F>` where F is usually some unmentionable closure type).


The lazy collections are an interesting case, because as you build up a stack of transformations, the type itself grows in ways beyond simple A -> B type substitutions. That's a really good example of a case better served by GATs than HKTs -- they're not that kind of Functor to begin with.

To be explicit, a lazy collection type in Rust is more than just `List<T>` -- it needs to carry an additional type describing the set of transformations being applied. You'd really have something closer to `List<F, T>`, and as you apply transformations, F grows while T is substituted.

(You could avoid this by storing a trait object for your transformation stack instead of parametrizing over F, but half the point is that you can often flatten the whole stack down into highly-efficient generated code, and abstracting over the precise F hinders that flattenability.)




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: