Using monads for trivial tasks like list manipulation?

前端 未结 6 1406
栀梦
栀梦 2021-02-09 05:13

Whenever I read about Monad example, they always present IO as a case study.

Are there any examples of monads doing list manipulation which somebody could present? I apr

6条回答
  •  天涯浪人
    2021-02-09 06:15

    The big secret to the list monad in Haskell is that list comprehensions are syntactic sugar for do blocks. Any time you write a list comprehension, you could have written it using a do block instead, which uses the list monad instance.

    A simple example

    Let's say you want to take two lists, and return their cartesian product (that is, the list of (x,y) for every combination of x from the first list and y from the second list).

    You can do that with a list comprehension:

    ghci> [(x,y) | x <- [1,2], y <- [3,4]] -- [(1,3),(1,4),(2,3),(2,4)]
    

    The list comprehension is syntactic sugar for this do block:

    zs = do x <- [1,2]
            y <- [3,4]
            return (x,y)
    

    which in turn is syntactic sugar for

    zs = [1,2] >>= \x -> [3,4] >>= \y -> return (x,y)
    

    A more complicated example

    That example doesn't really demonstrate the power of monads, though, because you could easily write it without relying on the fact that lists have a Monad instance. For example, if we only use the Applicative instance:

    ghci> import Control.Applicative
    ghci> (,) <$> [1,2] <*> [3,4] -- [(1,3),(1,4),(2,3),(2,4)]
    

    Now let's say you take every element of a list of positive integers, and replicate it as many times as itself (so e.g. f [1,2,3] = [1,2,2,3,3,3] for example). Who knows why you'd want to do that, but it is easy:

    ghci> let f xs = [ y | x <- xs, y <- replicate x x ]
    ghci> f [1,2,3] -- [1,2,2,3,3,3]
    

    That's just syntactic sugar for this:

    f xs = do x <- xs
              y <- replicate x x
              return y
    

    which in turn is syntactic sugar for

    f xs = xs >>= \x -> replicate x x >>= \y -> return y
    

    This time we can't write that just using the applicative instance. The key difference is that we took the output from the first bind (x) and made the rest of the block depend on it (y <- replicate x x).

提交回复
热议问题