About the function monad

2019-04-22 04:57发布

问题:

I have some confusion with the function monad. The function monad is defined as follow:

instance Monad ((->) r) where
     return x = \_ -> x
     h >>= f = \w -> f (h w) w

I tried to play around with it by writing a binding operation:

( (*2) >>= (+10) ) 3 

(return 3) :: ((->) Int)

But it caused errors. And I also try to rewrite a function AddStuff into the binding operations.

addStuff = do
           a <- (*2)
           b <- (+10)
           return (a+b)

then convert this function into

addStuff' w = (*2)  w >>= (\a ->
              (+10) w >>= (\b ->
              return (a+b) ))

I check the type of the new function as see

addStuff :: (Monad m, Num (m b), Num b) => m b -> m b 

Why is that? How can I fix that?

回答1:

In addStuff' you write (*2) w and (+10) w. Those are equivalent to w*2 and w+10 respectively. So addStuff' is equivalent to this:

addStuff' w = w*2 >>= \a ->
              w+10 >>= \b ->
              return (a+b)

Writing it this way should make it obvious that here the left operands to >>= are numbers, not functions. That's why the inferred type is telling you that your function only works for numbers that are monads.

When eliminating do notation the left operand to >>= should be exactly the same as the right operand of <-. Also eliminating do notation does not add any arguments to the function. So the correct rewriting would look like this:

addStuff' = (*2) >>= \a ->
            (+10) >>= \b ->
            return (a+b)

As to why your earlier pieces of code don't work:

( (*2) >>= (+10) ) 3 

The operator >>= has type m a -> (a -> m b) -> m b. For simplicity let's assume that all the numbers in this code have type Int, then your left operand has type Int -> Int or m Int if m is (->) Int. So for some type b the right operand should have type Int -> ((->) Int) b or, more readably, Int -> Int -> b. The type it actually has though is Int -> Int. Therefore your expression is ill-typed.

(return 3) :: ((->) Int)

((->) Int) has kind * -> * - the type of a value must have kind *.

Or to approach this differently: return 3 has type m Int for some m (still assuming that all integer literals have type Int for simplicity). So if m is `((->) Int), the type of return 3 will be ((->) Int) Int or Int -> Int, not ((->) Int).