Why does he associate the argument with responsibility for its value?

A typical monad function bind

has the following signature:

m a -> (a -> m b) -> m b

      

As I understand it (and maybe I'm wrong), a function (a -> m b)

is simply a function to map from one structure a

to another b

. Assuming this is correct, it begs the question why the signature is bind

n't just:

m a -> (a -> b) -> m b

      

Given that unit

is part of the definition of a monad; why make a function (a -> m b)

call responsibility for unit

whatever value b

it has produced - wouldn't it make more sense to make it part of it bind

?

+2


source to share


5 answers


A function like this m a -> (a -> b) -> m b

would be equivalent fmap :: (a -> b) -> f a -> f b

. Anyone fmap

can do this by changing values ​​within an action, it cannot perform new actions. With, m a -> (a -> m b) -> m b

you can "run" m a

, pass that value to (a -> m b)

, and then return the new effect m b

. Without this, you could ever have one effect in your program, you could not have two print statements in series, you could not connect to the network and then load a URL, and you could not respond to user input. you could only convert the value returned from each primitive operation. It is this operation that allows monads to be more powerful than functors or applications.



Another detail is that you are not necessarily just exchanging a value with unit

, which m b

can represent an action, and not just return something. For example, where is the call return

in action putStrLn :: String -> m ()

? This function signature is compatible with the second argument >>=

, a ~ String

and b ~ ()

, but there is no call return

anywhere in its body. The point >>=

is a sequence of two actions together, not just to carry values ​​in context.

+9


source


Because m a -> (a -> b) -> m b

- it's just fmap

that Monas has, being a functor. What Monad adds to the functor is the ability join

(or squash) of a nested Monad to be simple. Sample list list for simple list or [[1,2], [3]]

- [1,2,3]

.

If you change b

to m b

in the signature fmap

, you get

m a -> (a -> m b) -> m (m b)

      



With a normal functor, you are stuck in a double container layer ( m (m b)

). With Monad, using a function join

, you can squander m (m b)

on m b

. So, bind

actually join.fmap

.

In fact, join

and fmap

can only be written with bind

(and return

), so in practice it is easier to define only one function bind

instead of two, join

and fmap

although it is often easier to write latners.

Basically, it bind

is a combination of fmap

and join

.

+6


source


As I understand it (and maybe I am wrong) a function (a -> m b)

is only a function of mapping from one structure a

to anotherb

You are absolutely correct on this point - if you change the word "mapping" to morphism. For monad a -> m b

morphism functions, the category is Kleisli . In this light, the salient feature of monads is that you can compose Kleislis in the same way that you can create functions:

type Kleisli m a b = a -> m b  -- `Control.Arrow` has this as a `newtype` with `Category` instance.

-- compare (.) :: (b->c)     ->    (a->b)     ->     a->c
(<=<) :: Kleisli m b c -> Kleisli m a b -> Kleisli m a c
(f<=<g) x = f =<< g x

      

Alternatively, you can use regular functions like Kleislis:

(return.) :: (a->b) -> Kleisli m a b

      

However, Kleislis is strictly stronger than functions. For example. for m ≑ IO

they are basically functions that can have side effects that normal Haskell functions can't. So you can't turn Kleisli into a function - and if >>=

accepted a->b

and not a Kleisli m a b

, but all you had was Kleisli, there would be no way to use it.

+4


source


A type function a -> m b

is potentially much more powerful than one of the types a -> b

that follows return

(or, as you call it, "unit"). In fact, no "spectacular" operation can be expressed in the latter form.

+2


source


Another thing: any useful monad will have a number of operations specific to it, in addition to those that come from the monadic interface. For example, the monad IO

has getLine :: IO String

. Consider this very simple program:

main :: IO ()
main = do name <- prompt "What your name?"
          putStrLn ("Hello " ++ name ++ "!")

prompt :: String -> IO String
prompt str = do putStrLn str
                getLine

      

Note that the type prompt

matches the shape a -> m b

, but it doesn't use return

(aka unit

) anywhere. This is because it uses getLine :: IO String

, the opaque operation provided by the monad IO

and which cannot be defined in terms of return

and >>=

.

Think of it this way: ultimately Monad

never used by yourself; it is an interface for connecting things that are external to it, for example getLine

and putStrLn

.

+1


source







All Articles