Haskell Monad - How does Monad on list work?

前端 未结 3 703
旧时难觅i
旧时难觅i 2020-12-04 03:04

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 ->         


        
相关标签:
3条回答
  • 2020-12-04 03:52

    List comprehensions are just like nested loops:

       xs >>| foo = [ y | x <- xs, y <- foo x]
    
    --            =   for x in xs:
    --                         for y in (foo x):
    --                               yield y
    

    Thus we have

    [1,2,3,4] >>| (\x -> [x, x+10])
    =
    [ y | x <- [1,2,3,4], y <- (\x -> [x, x+10]) x]
    =
    [ y | x <- [1] ++ [2,3,4], y <- [x, x+10]]
    =
    [ y | x <- [1], y <- [x, x+10]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]  -- (*)
    =
    [ y |           y <- [1, 1+10]]   ++ [ y | x <- [2,3,4], y <- [x, x+10]]
    =
    [ y | y <- [1]] ++ [ y | y <- [11]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
    =
    [1] ++ [11] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
    =
    [1, 11] ++ [2, 12] ++ [ y | x <- [3,4], y <- [x, x+10]]
    =
    [1, 11] ++ [2, 12] ++ [3, 13] ++ [ y | x <- [4], y <- [x, x+10]]
    =
    [1, 11] ++ [2, 12] ++ [3, 13] ++ [4, 14]
    

    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:

      [ a,          [a1, a2] ++        concat [ [a1, a2],         [  a1, a2,
        b,    ==>   [b1]     ++    ==           [b1],        ==      b1,
        c,          []       ++                 [],
        d ]         [d1, d2]                    [d1, d2] ]           d1, d2  ]
    

    The above is equivalent to

        concat (map foo [a,b,c,d]) 
        =  
        foo a ++ foo b ++ foo c ++ foo d
    

    for some appropriate foo.

    concat is list monad's join, and map is list monad's fmap. In general, for any monad,

        m >>= foo  =  join (fmap foo m)
    

    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:

    [     a     ,  b   ,  c  ,    d      ]
        /   \      |      |     /   \
    [  [a1, a2] , [b1] ,  [] , [d1, d2]  ]  -- fmap foo    = [foo x | x <- xs]
                                            --             =     [y | x <- xs, y <- [foo x]]
    [   a1, a2  ,  b1  ,        d1, d2   ]  -- join (fmap foo) = [y | x <- xs, y <-  foo x ]
    
    0 讨论(0)
  • 2020-12-04 03:54

    Monads are often easier understood with the “mathematical definition”, than with the methods of the Haskell standard class. Namely,

    class Applicative' m => Monadd m where
      join :: m (m a) -> m a
    

    Note that you can implement the standard version in terms of this, vice versa:

    join mma = mma >>= id
    
    ma >>= f = join (fmap f ma)
    

    For lists, join (aka concat) is particularly simple:

    join :: [[a]] -> [a]
    join xss = [x | xs <- xss, x <- xs]  -- xss::[[a]], xs::[a]
    -- join [[1],[2]] ≡ [1,2]
    

    For the example you find confusing, you'd have

    [1,2,3,4] >>= \x->[(x+1)]
      ≡   join $ fmap (\x->[(x+1)]) [1,2,3,4]
      ≡   join [[1+1], [2+1], [3+1], [4+1]]
      ≡   join [[2],[3],[4],[5]]
      ≡   [2,3,4,5]
    
    0 讨论(0)
  • 2020-12-04 03:57

    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 with
    • concatMap :: (a -> [b]) -> [a] -> [b] for m = [].

    The regular (>>=) bind operator has the arguments flipped, but is otherwise just an infix concatMap.

    Or quite simply my confusion seems to stem from not understanding how this statement actually works:

    (>>|) xs f = [ y | x <- xs, y <- f x ]
    

    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:

    • Removing syntactic sugar: List comprehension in Haskell

    In your case, the statement could be written in a number of other ways:

    • Using do-notation:

      (>>|) xs f = do x <- xs
                      y <- f x
                      return y
      
    • De-sugared into using the (>>=) operator:

      (>>|) xs f = xs >>= \x ->
                   f x >>= \y ->
                   return y
      
    • This can be shortened (one rewrite per line):

        (>>|) xs f = xs >>= \x -> f x >>= \y -> return y -- eta-reduction
      ≡ (>>|) xs f = xs >>= \x -> f x >>= return         -- monad identity
      ≡ (>>|) xs f = xs >>= \x -> f x                    -- eta-reduction
      ≡ (>>|) xs f = xs >>= f                            -- prefix operator
      ≡ (>>|) xs f = (>>=) xs f                          -- point-free
      ≡ (>>|) = (>>=)
      

    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:

      instance Monadd [] where
        (>>|) xs f = concatMap f xs
      
    • Spelling that out a little more:

      instance Monadd [] where
        (>>|) xs f = concat (map f xs)
      
    • Spelling that out even more:

      instance Monadd [] where
        (>>|) [] f = []
        (>>|) (x:xs) f = let ys = f x in ys ++ ((>>|) xs f)
      

    The Monadd type class should have something similar to return. I'm not sure why it's missing.

    0 讨论(0)
提交回复
热议问题