Talk:Monads as containers
I am reading this article for the N-th time now, and though I'm quite a bit clearer on the subject, there still appears to be something at the corner of the picture that eludes me. Treating Monads as either Containers or Computations makes perfect sense, each on its own, but both ways brought together are void, somehow.
On the one hand, once you understand
bind, it seems to be more basic than
map, and its definition in terms of those seems a bit
far-fetched. On the other hand,
bind presents a significant semantic
threshold to overcome before you can say you understand what it does, while
map are much more straightforward.
Thus, the real problem seems to be reconciling both views. How about a "Computations as Containers" article? :)
Sorry for the late reply, I hadn't noticed this change until now. The basic connection is that a (parameterless) computation is a container for its results. To pass from a value of type (m a) to one of type a, possibly special circumstances will be necessary and extra context supplied to run the computation and extract the result from the computation/container. Sometimes, this will be statically impossible, as is the case with the IO monad.
The container/computation duality is a bit odd at first, but if you look at enough parallels in how things work, you'll essentially come to see it as just two ways of thinking about the same thing. Neither view is really perfect, but the correct description is rather abstract, so to work with it, you want some intuitive picture of what's happening.
Perhaps some clarification as to what
map do for computations
would be in order. I agree that for computations, bind is more
primitive-seeming than map and join, but map and join aren't all that bad
either. Map is fairly easy - mapping f over some computation gives the
computation which returns f of the result of the original computation. In
do-notation, this looks like: (I have something like this above, but here it
has the interpretive comments)
fmap f x = do r <- x -- run x return (f r) -- apply f to its result before returning it.
Join is conceptually slightly more bizarre, but still not so bad if you look
at the types:
join :: (Monad m) => m (m a) -> m a. It takes a computation
whose result is a computation, and produces the computation which runs that
computation and then runs its result, removing the level of indirection. So
in do notation, that looks like:
join x = do a <- x -- run x, getting its result, a r <- a -- run a, getting its result, r return r
or more simply:
join x = do a <- x -- run x a -- run its result
Looking at those examples is a good way to set up a correspondence of what's going on with the computations and what's going on with the containers. I'm not sure how much this helps, but really, being able to switch back and forth between the two analogies is enough to get a lot done.
hope this helps,
It does help, as far as it points the finger at the key notions to consider. The relationship is still elusive though, and I am growing ever more likely to think that the only proper way to appreciate something as fundamental as this is by rumination (I was hoping I could be spared of that :) This is partially reinforced by my impression that the topic seems to be the source of the same amount of confusion for just about anyone else who has approached it for the first time.
Thanks for the tips.
Well, the connection between the two analogies is important, but it's good to remember that you only really need to think of it in one way at a time. It's possible to get things done even if you only know one way to think about things. The reason that I put this page up is that I found that at first the idea of monads as abstractions of computation didn't work so well for me, so I needed to figure out my own analogy for the way that things worked, and containers is what worked for me. If you can understand the analogies separately, then you should have enough material to get work done, and together with a little experience in using them, to create your own monads. :)
Cale, the mental model i had initially corresponds most closely to "Monads as Tags" where you tag a value with the name of the monad and the tagged values are treated differently. Later I refreshed it to "Monads as environments" . This made more sense .. especially with phrases like " in the IO Monad" littered all over the place. i find it very difficult to think of Monads as just containers.
For those starting out with monads, it's probably helpful to actually have the Tree monad code, in case they want to "check their answer." Here it is:
instance Monad Tree where -- return :: a -> Tree a return = Leaf -- (>>=) :: Tree a -> ( a -> Tree b ) -> Tree b ( Leaf x ) >>= f = f x ( Branch l r ) >>= f = Branch ( l >>= f ) ( r >>= f )
And here are the other functions, for reference:
-- fmap :: ( a -> b ) -> Tree a -> Tree b fmap f ( Leaf x ) = Leaf ( f x ) fmap f ( Branch l r ) = Branch ( fmap f l ) ( fmap f r ) -- join :: Tree ( Tree a ) -> Tree a join ( Leaf t ) = t join ( Branch l r ) = Branch ( join l ) ( join r )
Unrelated to the above Monad Tree stuff, I was wondering if anyone had an idea of the kind of container a Continuation represents? :)
Kelan 22:49, 12 December 2007 (UTC)
I'm trying to understand what the tree monad might be used for. Can anyone provide an example?
-- Dangph 11:14, 12 January 2008 (UTC)
This is an excellent article, but I would advise learners to be aware of the limits of the Monads as Containers analogy.
The Monad comes from Category Theory. Mathematicians invented Category Theory because they needed a new language to take them past the point where their old analogies broke down.
The container analogy owes more to Set Theory than Category Theory. It's useful as a pair of training wheels but, ultimately, it must be discarded.
I think that the way forward is to find a way to teaching Monads that is informed by Category Theory, but doesn't actually teach Category Theory. (After all, we don't have to teach Group Theory to teach arithmetic.)
-- Peter McArthur 12:21, 20 August 2009 (UTC)
More details on the state monad
I know this is an old post and I hope you're still active here. I'd really like to understand the state monad in terms of map and join, but I'm completely mystified by
join :: (State s (State s a)) -> (State s a) join xss = State (\s -> uncurry runState (runState xss s))
I can mentally parse that, but I can't seem to get any intuition about what it's really doing, or why. I don't suppose it would be possible to expand in a bit more detail on the motivation for this code?