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
?
source to share
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.
source to share
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
.
source to share
As I understand it (and maybe I am wrong) a function
(a -> m b)
is only a function of mapping from one structurea
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.
source to share
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
.
source to share