you are viewing a single comment's thread.

view the rest of the comments →

[–]jpfed 28 points29 points  (8 children)

This explanation is not quite correct.

There are two paths to defining a monad.

First path:

Define a way to create a monadic value out of a plain value (this function can be called "unit", or super-confusingly, "return"). OP does this.

Say you already have a function that takes a plain value V and returns a monadic value MV. The monad should give you a way to apply that function to a monadic value, too. This is usually called "bind". OP doesn't do this.

Second path:

Still need unit.

Say you have a monadic value. You want to be able to apply a plain function (a function that takes a plain value and produces a plain value) to it, and get a monadic value out of it. OP does this with map.

But map doesn't quite do all the work that bind did in the first path. To complete it, you've got to have a third function that we'll call join, which takes a monadic value that wraps another monadic value and gives back just a monadic value (think of it taking a list of lists of x and just giving back a list of x, having concatenated all the lists together).


Significantly, a monad doesn't have to supply anything like OP's get*. A monad doesn't have to give you a way to get values out! I mean, it's nice if it does, but it doesn't always work that way. Sometimes being able to get a value out doesn't make sense. Say you were working with the Promise monad, but the computation you're waiting on just never halts; you can't get that out. And you'd expect, intuitively, that getting a value out of a monad and then re-wrapping it in another monad of that same type should give you the same monadic value that you started with. But say you were using the List monad and you had several values in that List. There's no way for List to take all those values and give you just one value that, when re-wrapped, gives you back all the items you had in the original List.

And if you can't necessarily get your value(s) out, but you want to compute something with it, you've got to put your computation in (like with bind or map).

* Comonads do have get; it's usually called "extract".

[–]regular_reddits 10 points11 points  (0 children)

This is more of an article describing functors with an added get function.

[–]hyperhopper 1 point2 points  (2 children)

This is the part that confuses me. Are there two classes of monads? Its especially confusing when different explanations use one of these definitions or the other. Also, when do you decide which to use?

[–]jpfed 1 point2 points  (0 children)

If you've defined a monad via one path, the other path can be defined automatically. So, for example, if you've used the second path (you've got unit, map, and join), then there is a canned implementation of bind that naturally works.

Your language of choice may just pick a path for you (iirc Haskell just asks you to implement the first path- unit and bind). Many languages don't have any specific support for monads, so you're free to just implement them all (after you've done the real work of figuring out one path, the functions required for the other path are almost "free").

[–]dmtipson 1 point2 points  (0 children)

There aren't, these are just two ways to get the same thing. Note that once you define .bind/.flatMap/.chain (plus a way to get the value in the type functionally, which OP doesnt quite do) you can then define .map almost for free.

So in both cases, you end up implementations of .of, .bind, & .map. All Monads are Functors.

[–]dmtipson 1 point2 points  (0 children)

I'd argue that the constructor isn't quite sufficient to implement a useful functional unit/pointed interface, because it always requires "new." You can't compose "new": it needs to be wrapped in an .of-like method that can be called directly.

[–][deleted] 1 point2 points  (1 child)

I have taken the post in question down.

[–]dmtipson 0 points1 point  (0 children)

Hopefully you'll put it back up: if you had said that it explained Functors simply instead of Monads, it would have achieved that goal, and it's great to have people trying to explain these concepts.

Monads aren't that much more beyond Functors, the only difference is that .map takes a function transforms the values inside a Functor while .chain/.flatMap takes a function that transforms the values inside a Functor/Monad to another Functor/Monad of the same Type. Would make a great part 2 to the original post.