70

Monads are usually explained in turns of return and bind. However, I gather you can also implement bind in terms of join (and fmap?)

In programming languages lacking first-class functions, bind is excruciatingly awkward to use. join, on the other hand, looks quite easy.

I'm not completely sure I understand how join works, however. Obviously, it has the [Haskell] type

join :: Monad m => m (m x) -> m x

For the list monad, this is trivially and obviously concat. But for a general monad, what, operationally, does this method actually do? I see what it does to the type signatures, but I'm trying to figure out how I'd write something like this in, say, Java or similar.

(Actually, that's easy: I wouldn't. Because generics is broken. ;-) But in principle the question still stands...)


Oops. It looks like this has been asked before:

Monad join function

Could somebody sketch out some implementations of common monads using return, fmap and join? (I.e., not mentioning >>= at all.) I think perhaps that might help it to sink in to my dumb brain...

Community
  • 1
  • 1
MathematicalOrchid
  • 58,942
  • 16
  • 110
  • 206
  • 3
    Does this snippet help? `join m = m >>= id` – Daniel Wagner Jun 27 '12 at 20:54
  • 1
    @DanielWagner I know _how_ to define `join`. At least, I know the incantation that yields the correct type signature. But I'm struggling a little to wrap my mind around what that literally _does_... – MathematicalOrchid Jun 27 '12 at 21:01
  • 9
    @MathematicalOrchid: There isn't really a general definition of join, in the same way that there isn't a general definition of bind. Both of them define what the monad *does*! Once you have defined them, you've picked out a particular monad. – porges Jun 27 '12 at 21:48
  • 3
    For whatever it is worth to you, here are some join/cojoin implementations in Java: * [reader monad](http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/Function.html#join%28fj.F%29) * [list monad](http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/data/List.html#join%28fj.data.List%29) * [rose-tree comonad](http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/data/Tree.html#cojoin%28%29) * [list-zipper comonad](http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/data/Zipper.html#cobind%28fj.F%29) PS: I suck arse at formatting, sorry. – Tony Morris Jun 28 '12 at 12:41
  • 1
    "For the list monad, this is trivially and obviously `concat`." Yes, `join == concat` for lists, but in what senses do you mean "trivially and obviously" so? Do you mean that there are no other possibilities for `join` on lists; or that `concat` is one possibility that you know of that type-checks; or ...? – Conal Jun 30 '12 at 17:45
  • @Conal The type signature says we start with a list of lists and end up with a flat list. The "obvious" way to do that is to flatten the list. There might be other possibilities, but this is the obvious one. (Now that I think of it, some kind of "diagonal" join ought to be possible...) – MathematicalOrchid Jul 01 '12 at 10:28
  • 4
    @MathematicalOrchid Thanks. So I think you mean that `concat` is one easily spotted `join` candidate (given some rudimentary Haskell knowledge) rather than *the* (i.e., one & only) possibility. I raised my question out of concern that readers might take your remark literally and believe it to be true, thus missing some illuminating lines of inquiry. For instance, does `concat` satisfy the required monad laws? Do other candidates? If yes & no (respectively), how could one *derive* `concat` from the laws? I use such questions to lead me away from the "obvious", i.e., my blind spots. – Conal Jul 02 '12 at 18:10
  • Diagonal join (by which I mean, your nth element is the nth element of the nth list) is the way to do it for infinite streams, at least, and might also do the trick in the finite case, with some constraints on the list (non-empty, perhaps?) – Ben Millwood Jul 05 '12 at 17:54
  • How could diagonalization work as a monadic join? It isn't associative, since it depends on global context. – misterbee Jan 13 '14 at 02:16
  • OTOH, `join (x:_) = if x == [] then x else head x` ; join [] = []` works, as that is just an embedding of `Maybe` in `List` that handles cases that are not expressible in `Maybe` – misterbee Jan 13 '14 at 02:37

7 Answers7

103

Without plumbing the depths of metaphor, might I suggest to read a typical monad m as "strategy to produce a", so the type m value is a first class "strategy to produce a value". Different notions of computation or external interaction require different types of strategy, but the general notion requires some regular structure to make sense:

  • if you already have a value, then you have a strategy to produce a value (return :: v -> m v) consisting of nothing other than producing the value that you have;
  • if you have a function which transforms one sort of value into another, you can lift it to strategies (fmap :: (v -> u) -> m v -> m u) just by waiting for the strategy to deliver its value, then transforming it;
  • if you have a strategy to produce a strategy to produce a value, then you can construct a strategy to produce a value (join :: m (m v) -> m v) which follows the outer strategy until it produces the inner strategy, then follows that inner strategy all the way to a value.

Let's have an example: leaf-labelled binary trees...

data Tree v = Leaf v | Node (Tree v) (Tree v)

...represent strategies to produce stuff by tossing a coin. If the strategy is Leaf v, there's your v; if the strategy is Node h t, you toss a coin and continue by strategy h if the coin shows "heads", t if it's "tails".

instance Monad Tree where
  return = Leaf

A strategy-producing strategy is a tree with tree-labelled leaves: in place of each such leaf, we can just graft in the tree which labels it...

  join (Leaf tree) = tree
  join (Node h t)  = Node (join h) (join t)

...and of course we have fmap which just relabels leaves.

instance Functor Tree where
  fmap f (Leaf x)    = Leaf (f x)
  fmap f (Node h t)  = Node (fmap f h) (fmap f t)

Here's an strategy to produce a strategy to produce an Int.

tree of trees

Toss a coin: if it's "heads", toss another coin to decide between two strategies (producing, respectively, "toss a coin for producing 0 or producing 1" or "produce 2"); if it's "tails" produce a third ("toss a coin for producing 3 or tossing a coin for 4 or 5").

That clearly joins up to make a strategy producing an Int.

enter image description here

What we're making use of is the fact that a "strategy to produce a value" can itself be seen as a value. In Haskell, the embedding of strategies as values is silent, but in English, I use quotation marks to distinguish using a strategy from just talking about it. The join operator expresses the strategy "somehow produce then follow a strategy", or "if you are told a strategy, you may then use it".

(Meta. I'm not sure whether this "strategy" approach is a suitably generic way to think about monads and the value/computation distinction, or whether it's just another crummy metaphor. I do find leaf-labelled tree-like types a useful source of intuition, which is perhaps not a surprise as they're the free monads, with just enough structure to be monads at all, but no more.)

PS The type of "bind"

(>>=) :: m v -> (v -> m w) -> m w

says "if you have a strategy to produce a v, and for each v a follow-on strategy to produce a w, then you have a strategy to produce a w". How can we capture that in terms of join?

mv >>= v2mw = join (fmap v2mw mv)

We can relabel our v-producing strategy by v2mw, producing instead of each v value the w-producing strategy which follows on from it — ready to join!

pigworker
  • 42,162
  • 18
  • 118
  • 213
  • so a join/concat joins small sub-trees to create a larger one.... great explanation – Jamil Jun 28 '12 at 10:03
  • 13
    @Jamil if you found this helpful, you should check out the article [Monads Are Trees With Grafting](https://github.com/Mzk-Levi/texts/blob/master/Monads%20are%20Trees%20with%20Grafting.pdf) by Dan Piponi. It shows how you can view *all* monads in this way. – Chris Taylor Jun 28 '12 at 10:15
  • 8
    @Jamil (Thanks!) In the list monad, a "strategy to produce a value" consists of offering a choice of values. `concat` turns a choice between choices of things into a choice of things, like turning a street full of shops (first choose shop, then choose stuff) into one big supermarket. – pigworker Jun 28 '12 at 10:33
  • 1
    @Chris Taylor thanks for the link, I find this approach particularly interesting because I am half way through reading [Why free monads matter](http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html) – Jamil Jun 28 '12 at 12:21
  • nice exposition of: bind = join $ fmap – Carter Tazio Schonwald Jun 28 '12 at 21:02
  • how does `mv >>= v2mw = join (fmap v2mw mv)` type check for the second half? Passing in `v2mw` (`a -> m b`) doesn't match `fmap's` signature, right: `Functor f => (a -> b) -> f a -> f b`? – Kevin Meredith Aug 29 '14 at 12:19
  • 3
    Yes it does. We have `v2mw :: v -> m w` and `fmap :: (a -> b) -> m a -> m b` so `fmap v2mw` typechecks with `a = v` and `b = m w` giving type `m v -> m (m w)`, which is why the `join` afterwards gives you `m w`. – pigworker Aug 29 '14 at 13:09
  • 3
    As far as natural language metaphors go "strategy to produce a value" sounds less cluncky than "a value in a context". – szablica Aug 05 '15 at 08:45
  • 3
    Chris Taylor's link was from [Dan Piponi](http://blog.sigfpe.com/2010/01/monads-are-trees-with-grafting.html)'s blog. The link is dead now. Dan Piponi gives an [updated link](https://github.com/dpiponi/grafting3/blob/master/monads.pdf). – leewz Jan 14 '19 at 18:44
31
join = concat -- []
join f = \x -> f x x -- (e ->)
join f = \s -> let (f', s') = f s in f' s' -- State
join (Just (Just a)) = Just a; join _ = Nothing -- Maybe
join (Identity (Identity a)) = Identity a -- Identity
join (Right (Right a)) = Right a; join (Right (Left e)) = Left e; 
                                  join (Left e) = Left e -- Either
join ((a, m), m') = (a, m' `mappend` m) -- Writer
-- N.B. there is a non-newtype-wrapped Monad instance for tuples that
-- behaves like the Writer instance, but with the tuple order swapped
join f = \k -> f (\f' -> f' k) -- Cont
Daniel Wagner
  • 128,625
  • 9
  • 198
  • 347
19

Calling fmap (f :: a -> m b) (x ::ma) produces values (y ::m(m b)) so it is a very natural thing to use join to get back values (z :: m b).

Then bind is defined simply as bind ma f = join (fmap f ma), thus achieving the Kleisly compositionality of functions of (:: a -> m b) variety, which is what it is really all about:

ma `bind` (f >=> g) = (ma `bind` f) `bind` g              -- bind = (>>=)
                    = (`bind` g) . (`bind` f) $ ma 
                    = join . fmap g . join . fmap f $ ma

And so, with flip bind = (=<<), we have

    ((g <=< f) =<<)  =  (g =<<) . (f =<<)  =  join . (g <$>) . join . (f <$>)

Kleisli composition

Will Ness
  • 62,652
  • 8
  • 86
  • 167
17

OK, so it's not really good form to answer your own question, but I'm going to note down my thinking in case it enlightens anybody else. (I doubt it...)

If a monad can be thought of as a "container", then both return and join have pretty obvious semantics. return generates a 1-element container, and join turns a container of containers into a single container. Nothing hard about that.

So let us focus on monads which are more naturally thought of as "actions". In that case, m x is some sort of action which yields a value of type x when you "execute" it. return x does nothing special, and then yields x. fmap f takes an action that yields an x, and constructs an action that computes x and then applies f to it, and returns the result. So far, so good.

It's fairly obvious that if f itself generates an action, then what you end up with is m (m x). That is, an action that computes another action. In a way, that's maybe even simpler to wrap your mind around than the >>= function which takes an action and a "function that produces an action" and so on.

So, logically speaking, it seems join would run the first action, take the action it produces, and then run that. (Or rather, join would return an action that does what I just described, if you want to split hairs.)

That seems to be the central idea. To implement join, you want to run an action, which then gives you another action, and then you run that. (Whatever "run" happens to mean for this particular monad.)

Given this insight, I can take a stab at writing some join implementations:

join Nothing = Nothing
join (Just mx) = mx

If the outer action is Nothing, return Nothing, else return the inner action. Then again, Maybe is more of a container than an action, so let's try something else...

newtype Reader s x = Reader (s -> x)

join (Reader f) = Reader (\ s -> let Reader g = f s in g s)

That was... painless. A Reader is really just a function that takes a global state and only then returns its result. So to unstack, you apply the global state to the outer action, which returns a new Reader. You then apply the state to this inner function as well.

In a way, it's perhaps easier than the usual way:

Reader f >>= g = Reader (\ s -> let x = f s in g x)

Now, which one is the reader function, and which one is the function that computes the next reader...?

Now let's try the good old State monad. Here every function takes an initial state as input but also returns a new state along with its output.

data State s x = State (s -> (s, x))

join (State f) = State (\ s0 -> let (s1, State g) = f s0 in g s1)

That wasn't too hard. It's basically run followed by run.

I'm going to stop typing now. Feel free to point out all the glitches and typos in my examples... :-/

Yuval Itzchakov
  • 136,303
  • 28
  • 230
  • 296
MathematicalOrchid
  • 58,942
  • 16
  • 110
  • 206
  • 6
    I only spotted one error - lacking parens in `join (Just mx)`. Also, it's not bad form to answer your own question, cf. http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/ – Ben Millwood Jul 05 '12 at 18:04
13

I've found many explanations of monads that say "you don't have to know anything about category theory, really, just think of monads as burritos / space suits / whatever".

Really, the article that demystified monads for me just said what categories were, described monads (including join and bind) in terms of categories, and didn't bother with any bogus metaphors:

I think the article is very readable without much math knowledge required.

solrize
  • 131
  • 2
4

Asking what a type signature in Haskell does is rather like asking what an interface in Java does.

It, in some literal sense, "doesn't". (Though, of course, you will typically have some sort of purpose associated with it, that's mostly in your mind, and mostly not in the implementation.)

In both cases you are declaring legal sequences of symbols in the language which will be used in later definitions.

Of course, in Java, I suppose you could say that an interface corresponds to a type signature which is going to be implemented literally in the VM. You can get some polymorphism this way -- you can define a name that accepts an interface, and you can provide a different definition for the name which accepts a different interface. Something similar happens in Haskell, where you can provide a declaration for a name which accepts one type and then another declaration for that name which treats a different type.

rdm
  • 570
  • 4
  • 15
2

This is Monad explained in one picture. The 2 functions in the green category are not composable, when being mapped to the blue category (strictly speaking, they are one category), they become composable. Monad is about turning a function of type T -> Monad<U> into a function of Monad<T> -> Monad<U>.

Monad explained in one picture.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
Dagang
  • 19,188
  • 24
  • 70
  • 109