Why does haskell's bind function take a function from non-monodic to monadic

I have some questions about defining a binding function (>>=)

in Haskell.

Because Haskell is a pure language, we can use Monad to handle side-effect operations. I think this strategy is similar to the fact that all actions can cause side effects in the other world, and we can control them from our "pure" haskell world, though do

or >>=

.

So when I look at the function definition >>=

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

      

a function is required (a -> m b)

, so the result of the m a

previous action can be "unpacked" to non-monodic a

in >>=

. The function then (a -> m b)

takes a

as its input and returns another monadic as its result m b

. Thanks to the bind function, I can work on monadic without introducing any side effects to pure Haskell codes.

My question is why are we using the function (a -> m b)

? In my opinion, a function m a -> m b

can do this as well. Is there a reason, or is it just because it is designed like this?

EDIT

From the comments I understand that it is difficult to extract a

from m a

. However, I think I can treat monadic m a

as a

a side effect.

Is it safe to assume that the function m a -> m b

acts in a similar way a -> b

, so we can define m a -> m b

as a definition a -> b

?

+3


source to share


1 answer


edit2: ok, here's what I should have said from the start:

Monads are EDSL,

E as in embedded domain-specific languages. Inline means that language statements are simple values ​​in our language, Haskell.

Let's try our IO language. Imagine we have print1 :: IO()

describing the action of printing an integer 1

in an invitation. Imagine we also have print2 :: IO()

. Both are simple Haskell values. In Haskell we talk about these actions. This I / O language still has to be interpreted / processed by some part of the run-time system later, with "run" -time. With two languages, we have two worlds, two timelines.

We could write do { print1; print2 }

do { print1; print2 }

to describe compound actions. But we cannot create a new printable primitive 3

because it is outside of our pure Haskell world. We have EDSL, but obviously not very powerful. We must have an infinite number of primitives here; not a winning offer. And this is not even a Functor, since we cannot change these values.

Now what if we could? Then we can say do { print1; print2; fmap (1+) print2 }

do { print1; print2; fmap (1+) print2 }

do { print1; print2; fmap (1+) print2 }

to print as well 3

. It is now a Functor. More powerful, but not flexible enough.

We gain flexibility with primitives to create these action descriptors (for example print1

). Eg print :: Show a => a β†’ IO a

. Now we can talk about more versatile actions such as do { print 42; getLine; putStrLn ("Hello, " ++ "... you!") }

do { print 42; getLine; putStrLn ("Hello, " ++ "... you!") }

do { print 42; getLine; putStrLn ("Hello, " ++ "... you!") }

.

But now we see the need to refer to the "results" of previous actions. We want to be able to write do { print 42; s <- getLine; putStrLn ("Hello, " ++ s ++ "!") }

do { print 42; s <- getLine; putStrLn ("Hello, " ++ s ++ "!") }

do { print 42; s <- getLine; putStrLn ("Hello, " ++ s ++ "!") }

. We want to create (in the Haskell world) new action descriptions (Haskell values ​​describing actions in the IO world) based on the results (in the Haskell world) of the previous IO actions that these IO actions will take when they are done when the IO the language is interpreted (the actions it describes were performed in the IO world).

This means being able to create these IO language statements from Haskell values, such as c print :: a β†’ IO a

. And this is exactly the type you are asking about and that is what makes this EDSL a Monad.


Imagine we have an IO ( a_primitive :: IO Int β†’ IO()

) primitive that prints any positive integer as it is and prints it "---"

on a separate line before printing any non-positive integer. Then we could write a_primitive (return 1)

as you suggest.



But IO is closed; it is unclean; we cannot write new IO primitives in Haskell, and there cannot be a primitive already defined for any new idea that comes to our mind. So we write (\x β†’ if x > 0 then print x else do { putStrln "---"; print x })

instead and that type of lambda expression will be Int β†’ IO()

(more or less).

If the argument x

in the above lambda expression were of type IO Int

, the expression x > 0

would be sealed, and there is no way to get a

from IO a

directly without using the standard operator >>=

,

see also:

And this quote :

"Someone remarked at some point," Oh, to get impure effects from pure code, I need to do metaprogramming, which means that one of my types must be "programs that evaluate X". I want to take a "program that computes X" and a function that takes X and produces the next program, "a program that computes Y", and somehow glue them together into a "program that computes Y" "(which is an operation bind

). The IO monad was born. "


edit: these are four types of apps:

($)   ::                     (a ->   b) ->   a ->   b
(<$>) :: Functor f     =>    (a ->   b) -> f a -> f b
(<*>) :: Applicative f =>  f (a ->   b) -> f a -> f b
(=<<) :: Monad f       =>    (a -> f b) -> f a -> f b

      

 a                f a                f  a                f a
 a -> b           a   -> b           f (a -> b)            a -> f b
 ------           --------           ----------          ----------
      b           f      b           f       b           f        b

      

What for? They just are. Your question really, why do we need monads? Why aren't functors or applicative functors enough? And this, of course, has already been asked and answered many times (for example, the 2nd link in the list just above). First, as I tried to show above, monads allow us to code new computations in Haskell.

+6


source







All Articles