In order to understand Monad, I came up with the following definitions:
class Applicative' f where
purea :: a -> f a
app :: f (a->b) -> f a -> f b
class Applicative' m => Monadd m where
(>>|) :: m a -> (a -> m b) -> m b
instance Applicative' [] where
purea x = [x]
app gs xs = [g x | g <- gs, x <- xs]
instance Monadd [] where
(>>|) xs f = [ y | x <-xs, y <- f x]
It works as expected:
(>>|) [1,2,3,4] (\x->[(x+1)])
[2,3,4,5]
I am not sure how it is working though. For example:
[ y | y <- [[1],[2]]]
[[1],[2]]
How does application (\x->([x+1])
to each list element of [1,2,3]
result in [2,3,4]
and not [[2],[3],[4]]
Or quite simply my confusion seems to stem from not understanding how this statement [ y | x <-xs, y <- f x]
actually works
List comprehensions are just like nested loops:
Thus we have
The crucial step is marked
(*)
. You can take it as the definition of what list comprehensions are.A special case is when the
foo
function returns a singleton list, like in your question. Then it is indeed tantamount to mapping, as each element in the input list is turned into one (transformed) element in the output list.But list comprehensions are more powerful. An input element can also be turned conditionally into no elements (working as a filter), or several elements:
The above is equivalent to
for some appropriate
foo
.concat
is list monad'sjoin
, andmap
is list monad'sfmap
. In general, for any monad,The essence of Monad is: from each entity "in" a "structure", conditionally producing new elements in the same kind of structure, and splicing them in-place:
Monads are often easier understood with the “mathematical definition”, than with the methods of the Haskell standard class. Namely,
Note that you can implement the standard version in terms of this, vice versa:
For lists,
join
(akaconcat
) is particularly simple:For the example you find confusing, you'd have
Wadler, School of Haskell, LYAH, HaskellWiki, Quora and many more describe the list monad.
Compare:
(=<<) :: Monad m => (a -> m b) -> m a -> m b
for lists withconcatMap :: (a -> [b]) -> [a] -> [b]
form = []
.The regular
(>>=)
bind operator has the arguments flipped, but is otherwise just an infixconcatMap
.Since list comprehensions are equivalent to the Monad instance for lists, this definition is kind of cheating. You're basically saying that something is a Monadd in the way that it's a Monad, so you're left with two problems: Understanding list comprehensions, and still understanding Monad.
List comprehensions can be de-sugared for a better understanding:
In your case, the statement could be written in a number of other ways:
Using do-notation:
De-sugared into using the
(>>=)
operator:This can be shortened (one rewrite per line):
So from using list comprehensions, you haven't really declared a new definition, you're just relying on the existing one. If you wanted, you could instead define your
instance Monadd []
without relying on existing Monad instances or list comprehensions:Using
concatMap
:Spelling that out a little more:
Spelling that out even more:
The Monadd type class should have something similar to
return
. I'm not sure why it's missing.