How does the List monad work in this example?

匿名 (未验证) 提交于 2019-12-03 01:02:01

问题:

The List monad has return x = [x]. So why in the following example is the result not [(["a", "b"], [2, 3])]?

> pairs a b = do { x <- a; y <- b; return (x, y)} > pairs ["a", "b"] [2,3] [("a",2),("a",3),("b",2),("b",3)] 

回答1:

Let us first analyze and rewrite the function pairs:

pairs a b = do { x <- a; y <- b; return (x, y)} 

Here we thus have a monad. We use do as syntactical sugar. But the compiler rewrites this to:

pairs a b = a >>= (\x -> b >>= (\y -> return (x, y))) 

Or in a more canonical form:

pairs a b = (>>=) a (\x -> (>>=) b (\y -> return (x, y))) 

Now the list monad is defined as:

instance Monad [] where     return x = [x]     (>>=) xs f = concatMap f xs 

So we have written:

pairs a b = concatMap (\x -> concatMap (\y -> [(x, y)]) b) a 

So we thus take as input two lists a and b, and we perform a concatMap on a with as function (\x -> concatMap (\y -> [(x, y)]) b). In that function we perform another concatMap on b with as function \y -> [(x, y)].

So if we evaluate this with pairs ["a", "b"] [2,3] we get:

   pairs ["a", "b"] [2,3] -> concatMap (\x -> concatMap (\y -> [(x, y)]) [2,3]) ["a", "b"] -> concatMap (\y -> [("a", y)]) [2,3] ++ concatMap (\y -> [("b", y)]) [2,3] -> [("a", 2)] ++ [("a", 3)] ++ [("b", 2)] ++ [("b", 3)] -> [("a", 2), ("a", 3), ("b", 2), ("b", 3)] 


回答2:

In general,

pairs a b = do { x <- a; y <- b; return (x, y) }             = do { x <- a;                     do { y <- b;                              do { return (x, y) }}} 

means, in pseudocode,

pairs( a, b) {  for x in a do:                       for y in b do:                                   yield( (x, y) );               } 

whatever the "for ... in ... do" and "yield" mean for the particular monad. More formally, it's

          = a  >>= (\x ->                     do { y <- b;                          -- a >>= k  ===                             do { return (x, y) }})        -- join (k <$> a)           = join ( (<$> a)                                --  ( a       :: m a                    (\x ->                                 --    k       ::   a ->   m b                     do { y <- b;                          --    k <$> a :: m       (m b) )                             do { return (x, y) }}) )      --            :: m          b 

( (<$>) is an alias for fmap).

For the Identity monad, where return a = Identity a and join (Identity (Identity a)) = Identity a, it is indeed

pairs( {Identity, a}, {Identity, b}) {  x = a;                                          y = b;                                          yield( {Identity, {Pair, x, y}} );                                       } 

For the list monad though, "for" means foreach, because return x = [x] and join xs = concat xs:

-- join :: m (m a) -> m a -- join :: [] ([] a) -> [] a -- join :: [[a]] -> [a] join = concat 

and so,

join    [ [a1, a2, a3, ...],           [b1, b2, b3, ...],            .....           [z1, z2, z3, ...] ] =         [  a1, a2, a3, ... ,            b1, b2, b3, ... ,             .....            z1, z2, z3, ...  ] 

Monadic bind satisfies ma >>= k = join (fmap k ma) where ma :: m a, k :: a -> m b for a Monad m. Thus for lists, where fmap = map, we have ma >>= k = join (fmap k ma) = concat (map k ma) = concatMap k ma:

m >>= k  =  [ a,        = join [ k a,   = join [ [ a1, a2, ... ],  =  [  a1, a2, ... ,               b,                 k b,            [ b1, b2, ... ],        b1, b2, ... ,               c,                 k c,            [ c1, c2, ... ],        c1, c2, ... ,               d,                 k d,            [ d1, d2, ... ],        d1, d2, ... ,               e,                 k e,            [ e1, e2, ... ],        e1, e2, ... ,              ... ] >>= k         ...  ]          ...............  ]      ...........   ] 

which is exactly what nested loops do. Thus

pairs ["a",                                   -- for x in ["a", "b"] do:        "b"] [2, 3]                            --          for y in [2, 3] do: =                                             --                     yield (x,y)       ["a",         "b"] >>= (\x-> join (fmap (\y -> return (x,y)) [2, 3]) ) =       ["a",         "b"] >>= (\x-> concat (map (\y -> [ (x,y) ]) [2, 3]) ) = join [ "a"  &   (\x-> concat ((\y -> [ (x,y) ]) `map` [2, 3]) ),      -- x & f = f x        "b"  &   (\x-> concat ((\y -> [ (x,y) ]) `map` [2, 3]) ) ]      = join [              concat ((\y -> [ ("a",y) ]) `map` [2, 3])  ,                         concat ((\y -> [ ("b",y) ]) `map` [2, 3])   ]      = join [ concat [ [("a", 2)], [("a", 3)] ] ,    -- for y in [2, 3] do: yield ("a",y)        concat [ [("b", 2)], [("b", 3)] ] ]    -- for y in [2, 3] do: yield ("b",y) = join [        [  ("a", 2) ,  ("a", 3)  ] ,               [  ("b", 2) ,  ("b", 3)  ] ] =      [           ("a", 2) ,  ("a", 3)    ,                  ("b", 2) ,  ("b", 3)    ] 

Loop unrolling is what nested loops do ⁄ are, and nested computations are the essence of Monad.

It is also interesting to notice that

 join                =                      =   [a1] ++            =  [a1] ++ join   [ [ a1, a2, ... ],    [ a1, a2, ... ] ++          [a2, ... ] ++         [ [a2, ...],     [ b1, b2, ... ],    [ b1, b2, ... ] ++     [ b1, b2, ... ] ++      [ b1, b2, ...],     [ c1, c2, ... ],    [ c1, c2, ... ] ++     [ c1, c2, ... ] ++      [ c1, c2, ...],     [ d1, d2, ... ],    [ d1, d2, ... ] ++     [ d1, d2, ... ] ++      [ d1, d2, ...],     [ e1, e2, ... ],    [ e1, e2, ... ] ++     [ e1, e2, ... ] ++      [ e1, e2, ...],     ............... ]   ...............        ...............         ..............  ] 

which goes to the heart of the "nested loops ⁄ yield" analogy. Monads are higher order monoids, "what's the problem?"



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!