Why can't a function accept a monadic value and return another monadic value?

Let's say that we have two monadic functions:

f :: a -> mb g :: b -> mc h :: a -> mc 

The binding function is defined as

 (>>=) :: ma -> (a -> mb) -> mb 

My question is: why can't we do something like below. Declare a function that takes a monadic value and returns another monadic value?

  f :: a -> mb g :: mb -> mc h :: a -> mc 

The binding function is defined as

 (>>=) :: ma -> (ma -> mb) -> mb 

What is haskell that limits a function from taking a monadic value as an argument?

EDITOR: I think I have not made my question clear. The fact is, when you compose functions using the binding operator, why is the second argument to the bind operator a function that takes a non-monodic value ( b )? Why can't it take a monadic value ( mb ) and return mc . Unless, when you are dealing with monads, and the function you create will always be of the following type.

  f :: a -> mb g :: b -> mc h :: a -> mc 

and h = f 'compose' g

I am trying to learn monads, and this is something that I cannot understand.

+4
source share
9 answers

Monad key way is to look inside ma type and see a ; but the restriction of the Monad key is that for monads there should be “unrecoverable” possible, that is, operations of the Monad type should not be sufficient to write a function of the Monad m => ma -> a type Monad m => ma -> a . (>>=) :: Monad m => ma -> (a -> mb) -> mb gives you exactly this ability.

But there is more than one way to achieve this. The Monad class can be defined as follows:

 class Functor f where fmap :: (a -> b) -> fa -> fb class Functor f => Monad m where return :: a -> ma join :: m (ma) -> ma 

You ask why we cannot have the function Monad m => ma -> (ma -> mb) -> mb . Well, given f :: a -> b , fmap f :: ma -> mb is basically that. But fmap alone does not give you the opportunity to "look inside" Monad m => ma , but cannot escape from it. However, join and fmap together give you this ability. (>>=) can be written in general terms with fmap and join :

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

This is actually a common trick for defining an instance of Monad when you have trouble defining for (>>=) -write join for your potential monad, and then use the generic definition (>>=) .


Well, that answers the question, "should it be what it is," part of the no question. But why is it so?

I cannot speak for Haskell designers, but I like to think of it this way: in Haskell's monastic programming, the main building blocks are these actions:

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

In general, these basic building blocks are of 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 - action without an argument, Monad m => a -> mb - action with one argument, etc.

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

 join (fmap putStrLn getLine) 

More generally (>>=) embodies a concept very similar to the "pipeline" of actions, and as such is a more useful operator for using monads as a kind of programming language.


The final thing: make sure you know the Control.Monad module. Although return and (>>=) are the main functions for monads, there are endless other higher-level functions that you can define using these two, and this module collects several dozen of the most common. Your code should not be transferred to a straitjacket on (>>=) ; it is an important building block that is useful both on its own and as a component for large building blocks.

+6
source

why can't we do something like below. Declare a function that takes a monadic value and returns another monadic value?

 f :: a -> mb g :: mb -> mc h :: a -> mc 

I have to understand that you want to write the following?

 compose :: (a -> mb) -> (mb -> mc) -> (a -> mc) compose fg = h where h = ??? 

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

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

Choose a specialization (.) With types x = a , y = mb and z = mc

 (.) :: (mb -> mc) -> (a -> mb) -> (a -> mc) 

Now flip the input order and you get the desired compose function

 compose :: (a -> mb) -> (mb -> mc) -> (a -> mc) compose = flip (.) 

Please note that we have not even mentioned the monad here. This works great for any type m constructor, whether it is a monad or not.


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

 composeM :: (a -> mb) -> (b -> mc) -> (a -> mc) 

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

Now the question is why. What distinguishes this composition from another, so that this requires that m be a monad and the other not? Well, the answer to this question lies at the heart of understanding what the Monad abstraction is, so I will leave a more detailed answer to the various Internet resources that talk about the subject. Suffice it to say that writing composeM impossible without knowing about m . Come on, give it a try. You simply cannot write this without any additional knowledge that m , and the additional knowledge necessary to write this function, is simply that m has a Monad structure.

+5
source

If you have two functions f :: ma -> mb and the monadic value x :: ma , you can simply apply fx . For this you do not need any special monadic operator, just a function application. But a function like f can never "see" a value of type a .

The monadic composition of functions is much stronger, and functions of the type a -> mb are the core of monadic calculations. If you have a monadic value of x :: ma , you cannot "get into it" to get some value of type a . But if you have a function f :: a -> mb that works with values ​​of type a , you can compose the value using the >>= function to get x >>= f :: mb . The fact is that f "sees" a value of type a and can work with it (but it cannot return it, it can return only one monadic value). This advantage is >>= , and each monad must ensure its correct implementation.

To compare two concepts:

  • If you have g :: ma -> mb , you can combine it with return to get g . return :: a -> mb g . return :: a -> mb (and then work with >>= ), but
  • not the other way around. In general, there is no way to create a function of type ma -> mb from a function of type a -> mb .

Thus, composite functions of types such as a -> mb are a more rigorous concept than composing functions of types such as ma -> mb .


For example: list monad represents calculations that can give a variable number of answers, including 0 answers (you can view it as non-deterministic calculations). The key elements of computation in the list monad are functions of the type a -> [b] . They make a certain contribution and give a variable number of answers. The composition of these functions takes the results from the first, applies the second function to each of the results, and combines it into one list of all possible answers.

Functions of the type [a] -> [b] will be different: they will represent calculations that take several input data and give multiple answers. They can be combined, but we get something less powerful than the original concept.


Perhaps an even more distinctive example is the IO monad. If you call getChar :: IO Char and use only functions like IO a -> IO b , you can never work with a character that has been read. But >>= allows you to combine such a value with a function of the type a -> IO b , which can "see" the character and do something with it.

+2
source

Let me rephrase your question a little:

Why can't I use functions like g :: ma -> mb with Monads?

Answer: we already do, with functions . There is nothing particularly “monadic” about fmap f :: Functor m => ma -> mb , where f :: a -> b . Monads are Functors; we get such functions using the good old fmap :

 class Functor fa where fmap :: (a -> b) -> fa -> fb 
+2
source

As others have noted, there is nothing that would limit a function to accept a monadic value as an argument. The bind function itself takes one, but not the function that is specified for the binding.

I think you can make it clear to yourself with the metaphor "Monad is a container." A good example for this might be. Although we know how to deploy value from Maybe conatiner, we do not know this for every monad, and in some monads (for example, IO) this is completely impossible. The idea is that Monad does this behind the scenes in a way you don't need to know. For example, you really need to work with the value that was returned in the IO monad, but you cannot expand it, so the function that does this should be in the IO monad itself.

+1
source

I like to think of the monad as a recipe for creating a program with a specific context. The power provided by the monad is the ability at any stage of your built program, depending on the previous value. The regular function >>= was chosen as the most useful interface for this branching ability.

As an example, the Maybe monad provides a program that can fail at a certain stage (the context is a failure state). Consider this psuedo-Haskell example:

 -- take a computation that produces an Int. If the current Int is even, add 1. incrIfEven :: Monad m => m Int -> m Int incrIfEven anInt = let ourInt = currentStateOf anInt in if even ourInt then return (ourInt+1) else return ourInt 

To maintain a branch based on the current result of a calculation, we must be able to access the current result. The above psuedo code would work if we had access to currentStateOf :: ma -> a , but this is usually not possible with monads. Instead, we write our decision on branching as a function of type a -> mb . Since a not in the monad in this function, we can consider it as a regular value, which is much easier to work with.

 incrIfEvenReal :: Monad m => m Int -> m Int incrIfEvenReal anInt = anInt >>= branch where branch ourInt = if even ourInt then return (ourInt+1) else return ourInt 

Thus, the type >>= really makes programming easier, but there are several alternatives that are sometimes more useful. It is noteworthy that the Control.Monad.join function, which in combination with fmap gives exactly the same power as >>= (or can be defined in terms of another).

+1
source

The reason (→ =) of the second argument does not accept the monad as input, because there is no need to bind such a function at all. Just apply it:

 m :: ma f :: a -> mb g :: mb -> mc h :: c -> mb (g (m >>= f)) >>= h 

You do not need (→ =) for g at all.

+1
source

The function can take a monadic value if it wants. But he is not forced to do this.

Consider the following far-fetched definitions using the list monad and functions from Data.Char:

 m :: [[Int]] m = [[71,72,73], [107,106,105,104]] f :: [Int] -> [Char] f mx = do g <- [toUpper, id, toLower] x <- mx return (g $ chr x) 

You can run m >>= f ; the result will be of type [Char] .

(It is important that m :: [[Int]] , and not m :: [Int] . >>= always “discards” one monadic layer from its first argument. If you do not want this, do fm instead of m >>= f .)

0
source

As already mentioned, nothing restricts the execution of such functions.

In fact, there is a large family of functions like :: ma -> (ma -> mb) -> mb :

 f :: Monad m => Int -> ma -> (ma -> mb) -> mb fnm mf = replicateM_ nm >>= mf m 

Where

 f 0 m mf = mf m f 1 m mf = m >> mf m f 2 m mf = m >> m >> mf m 

... etc....

(Note the base case: when n is 0, this is just a normal functional application.)

But what does this function do? It performs a monodic action several times, finally discarding all the results and returning the mf application to m.

Useful sometimes, but hardly generally useful, especially compared to >>= .

Google Quick Search gives no results; perhaps the result.

0
source

All Articles