in this post, we will going to examine the list a monad, first let's see what is the definition of the list monad, here is the definition of the
instance Monad [] where return x = [x] xs >>= f = concat (map f xs) fail _ = []
since list are also monad, and given the definition of the >>=, here is what you can get from the list monad.
ghci> [3,4,5] >>= \x -> [x,-x] [3,-3,4,-4,5,-5]
the [] is now the Nothing, as can be told by this:
ghci> [] >>= \x -> ["bad","mad","rad"] [] ghci> [1,2,3] >>= \x -> [] []
we can chain the [] as we do the Maybe type.
ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
this is like the list comprehension, if we write the code in the do notation.
listOfTuples :: [(Int,Char)] listOfTuples = do n <- [1,2] ch <- ['a','b'] return (n,ch)
and we can as well use the special syntax which has a name called "list comprehension".
ghci> [ (n,ch) | n <- [1,2], ch <- ['a','b'] ] [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
but the list comprehension allow us to filter the output ,an example is as follow.
ghci> [ x | x <- [1..50], '7' `elem` show x ] [7,17,27,37,47]
how does that translate to the list monad? we will need to see the guard method and the MonadPlus type class, first let's see the MonadPlus definition.
class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a
Monad Plus can also act as monoid. (not mean that it is a monoid?)
which is shown in the following MonadPlus [] instance definition.
instance MonadPlus [] where mzero = [] mplus = (++)
so, how is the guard method implememented?
guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
so we can have this:
ghci> guard (5 > 2) :: Maybe () Just () ghci> guard (1 > 2) :: Maybe () Nothing ghci> guard (5 > 2) :: [()] [()] ghci> guard (1 > 2) :: [()] []
so, we can implement filter with monad something like this:
ghci> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x) [7,17,27,37,47]
so combined with the >> method of the Monad, here is what it looks like:
ghci> guard (5 > 2) >> return "cool" :: [String] ["cool"] ghci> guard (1 > 2) >> return "cool" :: [String] []
Here's is the previous example rewritten in do notation.
sevensOnly :: [Int] sevensOnly = do x <- [1..50] guard ('7' `elem` show x) return x
remember we need to have the ending "return x". otherwise we will get the empty tuple.
let's me try to explain how that works? because guard work on bool and return a monoid, and List are monoid, so [1..50] are decomposed into [1] :[2] :[3] : [4],..,[50]. and each will be applied with the guard(),and that will return return(), or mzero based on the bool value.
As with other typeclass, there are some laws that guide over the monad typeclass.
- left identity
return x>>=f is the same as the f x
an example is as
ghci> return 3 >>= (\x -> Just (x+100000)) Just 100003 ghci> (\x -> Just (x+100000)) 3 Just 100003
- right identity
the second law states that if we have a monadic value and we can >>= to feed it to return , the results is our original monadic value, formally:
m >>= return is no different than just m
example is as follow.
we know that the list .>= is like this :
xs >>= f = concat (map f xs)
So when we feed [1,2,3,4] to return, first return gets mapped over [1,2,3,4], resulting in [[1],[2],[3],[4]]and then this gets concatenated and we have our original list.
Left identity and right identity are basically laws that describe how return should behave. It's an important function for making normal values into monadic ones and it wouldn't be good if the monadic value that it produced did a lot of other stuff.
- Associativity
The final monad law says that when we have a chain of monadic function applications with >>=, it shouldn't matter how they're nested. Formally written:
Doing (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g)
we can make a chain of calll like the following.
ghci> return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2 Just (2,4)
and if we parenthesize this, we 'd get .
ghci> ((return (0,0) >>= landRight 2) >>= landLeft 2) >>= landRight 2 Just (2,4)
but we can also write the routine like this:
return (0,0) >>= (\x -> landRight 2 x >>= (\y -> landLeft 2 y >>= (\z -> landRight 2 z)))
so, look at this rule like this:
it doesn't matter how you nest feeding values to monadic functions, what matters is their meaning. Here's another way to look at this law
consider the two function, f, and g, composing the two is like this:
(.) :: (b -> c) -> (a -> b) -> (a -> c) f . g = (\x -> f (g x))
but the value we are dealing with for Monadic value ? we cannot chain them together, but fortunately >>= to make that happen. so by using >>= , we can compose two monadic functions.
(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c) f <=< g = (\x -> g x >>= f)