Definition, Laws and Example

Possible Duplicate:
What is a monad?

I am learning to program in the functional language of Haskell and I came across Monads when studying parsers. I had never heard of them before and so I did some extra studying to find out what they are.

Everywhere I look in order to learn this topic just confuses me. I can't really find a simple definition of what a Monad is and how to use them. "A monad is a way to structure computations in terms of values and sequences of computations using those values" - eh???

Can someone please provide a simple definition of what a Monad is in Haskell, the laws associated with them and give an example?

  • Note: I know how to use the do syntax as I have had a look at I/O actions and functions with side-effects.

  • Intuition

    A rough intuition would be that a Monad is a particular kind of container ( Functor ), for which you have two operations available. A wrapping operation return that takes a single element into a container. An operation join that merges a container of containers into a single container.

    return :: Monad m => a -> m a     
    join   :: Monad m => m (m a) -> m a
    

    So for the Monad Maybe you have:

    return :: a -> Maybe a     
    return x = Just x
    
    join :: Maybe (Maybe a) -> Maybe a
    join (Just (Just x) = Just x
    join (Just Nothing) = Nothing 
    join Nothing        = Nothing
    

    Likewise for the Monad [ ] these operations are defined to be:

    return :: a -> [a]     
    return x = [x]
    
    join :: [[a]] -> [a]
    join xs = concat xs
    

    The standard mathematical definition of Monad is based on these return and join operators. However in Haskell the definition of the class Monad substitutes a bind operator for join.

    Monads in Haskell

    In functional programming languages these special containers are typically used to denote effectful computations. The type Maybe a would represent a computation that may or may not succeed, and the type [a] a computation that is non-deterministic. Particularly we're interested in functions with effects, iethose with types a->mb for some Monad m . And we need to be able to compose them. This can be done using either a monadic composition or bind operator.

    (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
    (>>=) :: Monad m => m a -> (a -> m b) -> m b
    

    In Haskell the latter is the standard one. Note that its type is very similar to the type of the application operator (but with flipped arguments):

    (>>=)    :: Monad m => m a -> (a -> m b) -> m b
    flip ($) ::              a -> (a ->   b) -> b
    

    It takes an effectful function f :: a -> mb and a computation mx :: ma returning values of type a , and performs the application mx >>= f . So how do we do this with Monads? Containers ( Functors ) can be mapped, and in this case the result is a computation within a computation which can then be flattened:

    fmap f mx        :: m (m b)
    join (fmap f mx) :: m b
    

    So we have:

    (mx >>= f) = join (fmap f mx) :: m b
    

    To see this working in practise consider a simple example with lists (non-deterministic functions). Suppose you have a list of possible results mx = [1,2,3] and a non-deterministic function fx = [x-1, x*2] . To calculate mx >>= f you begin by mapping mx with f and then you merge the results::

    fmap f mx                = [[0,2],[1,4],[2,6]]    
    join [[0,2],[1,4],[2,6]] = [0,2,1,4,2,6]
    

    Since in Haskell the bind operator (>>=) is more important than join , for efficiency reasons in the latter is defined from the former and not the other way around.

    join mx = mx >>= id
    

    Also the bind operator, being defined with join and fmap, can also be used to define a mapping operation. For this reason Monads are not required to be instances of the class Functor. The equivalent operation to fmap is called liftM in the Monad library.

    liftM f mx = mx >>= x-> return (f x) 
    

    So the actual definitions for the Monads Maybe becomes:

    return :: a -> Maybe a     
    return x = Just x
    
    (>>=)    :: Maybe a -> (a -> Maybe b) -> Maybe b
    Nothing >>= f = Nothing 
    Just x  >>= f = f x
    

    And for the Monad [ ]:

    return :: a -> [a]     
    return x = [x]
    
    (>>=)    :: [a] -> (a -> [b]) -> [b]
    xs >>= f = concat (map f xs)
             = concatMap f xs    -- same as above but more efficient
    

    When designing your own Monads you may find it easier to, instead of trying to directly define (>>=) , split the problem in parts and figure out what how to map and join your structures. Having map and join can also be useful to verify that your Monad is well defined, in the sense that it satisfy the required laws.

    Monad Laws

    Your Monad should be a Functor, so the mapping operation should satisfy:

    fmap id = id
    fmap g . fmap f = fmap (g . f)
    

    The laws for return and join are:

    join . return      = id
    join . fmap return = id  
    join . join = join . fmap join 
    

    The first two laws specify that merging undoes wrapping. If you wrap a container in another one, join gives you back the original. If you map the contents of a container with a wrapping operation, join again gives you back what you initially had. The last law is the associativity of join. If you have three layers of containers you get the same result by merging from the inside or the outside.

    Again you can work with bind instead of join and fmap. You get fewer but (arguably) more complicated laws:

    return a >>= f  = f a
    m >>= return    = m
    (m >>= f) >>= g = m >>= (x -> f x >>= g) 
    

    A monad in Haskell is something that has two operations defined:

    (>>=)  :: Monad m => m a -> (a -> m b) -> m b -- also called bind
    return :: Monad m => a -> m a
    

    These two operations need to satisfy certain laws that really might just confuse you at this point, if you don't have a knack for mathy ideas. Conceptually, you use bind to operate on values on a monadic level and return to create monadic values from "trivial" ones. For instance,

    getLine :: IO String ,

    so you cannot modify and putStrLn this String -- because it's not a String but an IO String !

    Well, we have an IO Monad handy, so not to worry. All we have to do is use bind to do what we want. Let's see what bind looks like in the IO Monad:

    (>>=) :: IO a -> (a -> IO b) -> IO b
    

    And if we place getLine at the left hand side of bind, we can make it more specific yet.

    (>>=) :: IO String -> (String -> IO b) -> IO b
    

    Okay, so getLine >>= putStrLn . (++ ". No problem after all!") getLine >>= putStrLn . (++ ". No problem after all!") would print the entered line with the extra content added. The right hand side is a function that takes a String and produces an IO () - that wasn't hard at all! We just go by the types.

    There are Monads defined for a lot of different types, for instance Maybe and [a] , and they behave conceptually in the same way.

    Just 2 >>= return . (+2) Just 2 >>= return . (+2) would yield Just 4 , as you might expect. Note that we had to use return here, because otherwise the function on the right hand side would not match the return type mb , but just b , which would be a type error. It worked in the case of putStrLn because it already produces an IO something, which was exactly what our type needed to match. (Spoiler: Expressions of shape foo >>= return . bar are silly, because every Monad is a Functor . Can you figure out what that means?)

    I personally think that this is as far as intuition will get you on the topic of monads, and if you want to dive deeper, you really do need to dive into the theory. I liked getting a hang of just using them first. You can look up the source for various Monad instances, for instance the List ( [] ) Monad or Maybe Monad on Hoogle and get a bit smarter on the exact implementations. Once you feel comfortable with that, have a go at the actual monad laws and try to gain a more theoretical understanding for them!


    Typeclassopedia有关于Monad的章节(但是请先阅读关于FunctorApplicative的前面章节)。

    链接地址: http://www.djcxy.com/p/47680.html

    上一篇: 单子的例子在哪里?

    下一篇: 定义,法律和例证