(>>=) :: Monad m => m a -> (a -> m b) -> m b
Why does the function need to be (a -> m b)? Why functions the preserve che inner data type (a -> m a) are not allowed?
You seem to have missed a basic principle behind type variables: you are allowed to instantiate type variables however you please with concrete types, provided they satisfy the typeclass constraints (here, whatever you choose for m
must be a Monad
) and are all correctly kinded (m
must have kind * -> *
and a
and b
must have kind *
).
So if you wanted to instantiate (>>=)
, some possible choices are:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>=) :: [a] -> (a -> [b]) -> [b]
(instantiating m
to []
)
(>>=) :: Monad m => m a -> (a -> m a) -> m a
(instantiating b
to a
)
(>>=) :: Monad m => m b -> (b -> m b) -> m b
(instantiating b
to a
... this is an equal type to the last one).
(>>=) :: Maybe Int -> (Int -> Maybe Int) -> Maybe Int
(>>=) :: Maybe Char -> (Char -> Maybe Int) -> Maybe Int
One last tidbit that can help understand the "shape" of the types for monad combinators. Look for types of the form a -> m b
where m
is a monad. These are called Kleisi arrows. If you were fanciful, you might imagine a different syntax for them, like a ~> b
meaning "a function from a
to b
with side effects allowed by some monad m
".
Looking at the type of bind, you see a raw m a
type at the front. This is essentially the same as () -> m a
: a function which takes "no inputs", and produces an a
with side effects in m
. The m b
at the end is similar.
If we rewrite this type in terms of these Kleisi arrows, we get:
(() -> m a) -> (a -> m b) -> (() -> m b)
Rewriting using our 'fanciful' syntax above, it becomes a little clearer:
(() ~> a) -> (a ~> b) -> (() ~> b)
If you squint, this ends up looking like function composition: You start off with ()
, do something and get to a
then do a second thing and get to b
. Combining those two steps into one, we just start with ()
and get to b
directly.