Reputation: 29538
I am trying to understand the expression below. It converts the list of characters ['a','b','c']
to a list of strings ["a", "b", "c"]
liftM (:[]) "abc"
How does this happen?
Upvotes: 6
Views: 979
Reputation: 34378
liftM
is equivalent to fmap
, only specialised to monads. (:[])
uses (:)
to make a function that produces lists of one element. Just like (+2)
is a compact way of writing (\x -> x + 2)
, (:[])
is equivalent to (\x -> x : [])
, or (\x -> [x])
.
Your expression, then, might have been written as:
fmap (\x -> [x]) "abc"
The existence of liftM
reflects the fact that any legitimate Monad
can be made into a Functor
by doing fmap f m = m >>= \x -> return (f x)
. You can always replace liftM
by fmap
, so the only reasons to use it are:
to define fmap
for free if you already have a Monad
instance (and don't want to use the DeriveFunctor
GHC extension), and
an entirely optional style choice (if you are writing obviously monadic code and feel that liftM
looks better than fmap
).
Upvotes: 8
Reputation: 27626
The robotic monkey head operator (:[])
is just the section of the list cons (:)
and the empty list []
, i.e. (:[])
is equivalent to (\x -> x:[])
; which in turn can also be written using list syntax as (\x -> [x])
.
Rewritten this way, we have
liftM (\x -> [x]) "abc"
The string literal "abc"
is also just syntax sugar for the character list ['a', 'b', 'c']
, so we can in turn rewrite the above as
liftM (\x -> [x]) ['a', 'b', 'c']
liftM
is just fmap
from the Dark Days when Functor
wasn't a superclass of Monad
, giving
fmap (\x -> [x]) ['a', 'b', 'c']
The Functor
instance of []
sets fmap = map
, giving
map (\x -> [x]) ['a', 'b', 'c']
which reduces to
[['a'], ['b'], ['c']]
Or, going back to string notation
["a", "b", "c"]
Q.e.d.
Upvotes: 14
Reputation: 85757
liftM
is defined as:
liftM f m = m >>= \x -> return (f x)
We're using liftM
with a list (of characters), so we need to look at the list instance of Monad
to see how >>=
and return
are defined:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
Thus
liftM f xs = xs >>= \x -> return (f x)
= concat (map (\x -> [f x]) xs)
The concat
on the outside and the [ ]
on the inside cancel each other out, so
liftM f xs = map (\x -> f x) xs
= map f xs
In other words, liftM
in the list monad is simply map
.
map (:[]) ['a', 'b', 'c'] = [(: []) 'a', (: []) 'b', (: []) 'c']
= ['a' : [], 'b' : [], 'c' : []]
= [['a'], ['b'], ['c']]
= ["a","b","c"]
because a string is really just a list of characters.
Upvotes: 6
Reputation: 1339
Function liftM
turns a function which takes input and produces output to a function which takes input in some monad and produces output in the same monad. Lets look at its definition:
liftM :: Monad m => (a -> b) -> m a -> m b
liftM f mx = mx >>= \x -> return (f x)
Strings in Haskell are lists of characters (type String = [Char]
), so
"abc" = ['a', 'b', 'c'] :: [Char]
From your application compiler infers a = Char
, b = [Char]
, m a = [Char]
, m = []
. So m b = [[Char]] = [String]
. List is a monad where return x = [x]
and (>>=) = concatMap
. So if we specialize above definition we get:
liftM f mx = concatMap (\x -> [f x]) mx
And if we apply the arguments we get:
concatMap (\x -> [[x]]) ['a', 'b', 'c'] =
concat $ map (\x -> [[x]]) ['a', 'b', 'c'] =
concat $ [[['a']], [['b']], [['c']]] =
[['a'], ['b'], ['c']] =
["a", "b", "c"]
Upvotes: 12