why can't a function take monadic value and return another monadic value?

Let's say that we have two monadic functions:

  f :: a -> m b
  g :: b -> m c
  h :: a -> m c

The bind function is defined as

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

My question is why can not we do something like below. Declare a function which would take a monadic value and returns another monadic value?

  f :: a -> m b
  g :: m b -> m c
  h :: a -> m c

The bind function is defined as

(>>=) :: m a -> (ma -> m b) -> m b

What is in the haskell that restricts a function from taking a monadic value as it's argument?

EDIT: I think I did not make my question clear. The point is, when you are composing functions using bind operator, why is that the second argument for bind operator is a function which takes non-monadic value ( b )? Why can't it take a monadic value ( mb ) and give back mc . Is it that, when you are dealing with monads and the function you would compose will always have the following type.

  f :: a -> m b
  g :: b -> m c
  h :: a -> m c

and h = f 'compose' g

I am trying to learn monads and this is something I am not able to understand.


A key ability of Monad is to "look inside" the ma type and see an a ; but a key restriction of Monad is that it must be possible for monads to be "inescapable," ie, the Monad typeclass operations should not be sufficient to write a function of type Monad m => ma -> a . (>>=) :: Monad m => ma -> (a -> mb) -> mb gives you exactly this ability.

But there's more than one way to achieve that. The Monad class could be defined like this:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Functor f => Monad m where
    return :: a -> m a
    join :: m (m a) -> m a

You ask why could we not have a Monad m => ma -> (ma -> mb) -> mb function. Well, given f :: a -> b , fmap f :: ma -> mb is basically that. But fmap by itself doesn't give you the ability to "look inside" a Monad m => ma yet not be able to escape from it. However join and fmap together give you that ability. (>>=) can be written generically with fmap and join :

(>>=) :: Monad m => m a -> (a -> m b) -> m b
ma >>= f = join (fmap f ma)

In fact this is a common trick for defining a Monad instance when you're having trouble coming up with a definition for (>>=) —write the join function for your would-be monad, then use the generic definition of (>>=) .


Well, that answers the "does it have to be the way it is" part of the question with a "no." But, why is it the way it is?

I can't speak for the designers of Haskell, but I like to think of it this way: in Haskell monadic programming, the basic building blocks are actions like these:

getLine :: IO String
putStrLn :: String -> IO ()

More generally, these basic building blocks have types that look like Monad m => ma , Monad m => a -> mb , Monad m => a -> b -> mc , ..., Monad m => a -> b -> ... -> mz . People informally call these actions. Monad m => ma is a no-argument action, Monad m => a -> mb is a one-argument action, and so on.

Well, (>>=) :: Monad m => ma -> (a -> mb) -> mb is basically the simplest function that "connects" two actions. getLine >>= putStrLn is the action that first executes getLine , and then executes putStrLn passing it the result that was obtained from executing getLine . If you had fmap and join and not >>= you'd have to write this:

join (fmap putStrLn getLine)

Even more generally, (>>=) embodies a notion much like a "pipeline" of actions, and as such is the more useful operator for using monads as a kind of programming language.


Final thing: make sure you are aware of the Control.Monad module. While return and (>>=) are the basic functions for monads, there's endless other more high-level functions that you can define using those two, and that module gathers a few dozen of the more common ones. Your code should not be forced into a straitjacket by (>>=) ; it's a crucial building block that's useful both on its own and as a component for larger building blocks.


why can not we do something like below. Declare a function which would take a monadic value and returns another monadic value?

f :: a -> m b
g :: m b -> m c
h :: a -> m c

Am I to understand that you wish to write the following?

compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose f g = h where
  h = ???

It turns out that this is just regular function composition, but with the arguments in the opposite order

(.) :: (y -> z) -> (x -> y) -> (x -> z)
(g . f) = x -> g (f x)

Let's choose to specialize (.) with the types x = a , y = mb , and z = mc

(.) :: (m b -> m c) -> (a -> m b) -> (a -> m c)

Now flip the order of the inputs, and you get the desired compose function

compose :: (a -> m b) -> (m b -> m c) -> (a -> m c)
compose = flip (.)

Notice that we haven't even mentioned monads anywhere here. This works perfectly well for any type constructor m , whether it is a monad or not.


Now let's consider your other question. Suppose we want to write the following:

composeM :: (a -> m b) -> (b -> m c) -> (a -> m c)

Stop. Hoogle time. Hoogling for that type signature, we find there is an exact match! It is >=> from Control.Monad, but notice that for this function, m must be a monad.

Now the question is why. What makes this composition different from the other one such that this one requires m to be a Monad, while the other does not? Well, the answer to that question lies at the heart of understanding what the Monad abstraction is all about, so I'll leave a more detailed answer to the various internet resources that speak about the subject. Suffice it to say that there is no way to write composeM without knowing something about m . Go ahead, try it. You just can't write it without some additional knowledge about what m is, and the additional knowledge necessary to write this function just happens to be that m has the structure of a Monad .


Let me paraphrase your question a little bit:

why can't don't we use functions of type g :: ma -> mb with Monads?

The answer is, we do already, with Functors . There's nothing especially "monadic" about fmap f :: Functor m => ma -> mb where f :: a -> b . Monads are Functors; we get such functions just by using good old fmap :

class Functor f a where
    fmap :: (a -> b) -> f a -> f b
链接地址: http://www.djcxy.com/p/43360.html

上一篇: 哈斯克尔monad:词源与意义?

下一篇: 为什么一个函数不能使用monadic值并返回另一个monadic值?