Considering the expression:
[0,1..] >>= \i -> [i * 2]
In the definition of >>=
for List, the lambda function \i -> [i * 2]
is mapped over the list argument via fmap, resulting in a list of lists, [[0], [2]..]
. So >>=
needs to flatten the result using a join function in order to return the list: [0, 2..]
According to this source: "...the definition of bind in terms of fmap and join works for every monad m: ma >>= k = join $ fmap k ma
"
So why is it necessary to place the burden of returning a monad on the function supplied to >>=? Why not simply define bind like so?
ma >>= k = fmap k ma
This way you don't have to deal with flattening the result.
What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
return is actually just a simple function in Haskell. It does not return something. It wraps a value into a monad. Looks like return is an overloaded function.
Bind takes a non-composable function f and returns a new function g that accepts the monadic type as input and returns the monadic type. g is composable. The unit function takes an argument of the type that f expected, and wraps it in the monadic type.
Monads are simply a way to wrapping things and provide methods to do operations on the wrapped stuff without unwrapping it. For example, you can create a type to wrap another one, in Haskell: data Wrapped a = Wrap a. To wrap stuff we define return :: a -> Wrapped a return x = Wrap x.
What you're proposing is to simply define the bind operator to be equal to fmap
, but with arguments swapped:
ma >>= k = fmap k ma
-- is equivalent to:
(>>=) = flip fmap
In which case, why not just use the fmap
itself, or its operator form <$>
?
(*2) <$> [0,1..]
> [0,2,4,6,...]
However, this does not cover all cases where bind
may be used. The difference is that monads are more powerful than functors. Where functors only let you produce exactly one output for every input, monads let you do all kinds of crazy stuff.
Consider, for example, the following:
[0,1,2,3] >>= \i -> [i*2, i*3]
> [0,0,2,3,4,6,6,9]
Here, the function produces two values for each input. This cannot be expressed via just fmap
. This requires join
ing the resulting values.
Here's another, even less obvious example:
[0,1,2,3,4,5] >>= \i -> if i `mod` 2 == 0 then [] else [i]
> [1,3,5]
Here, the function either produces a value or doesn't. The empty list is technically still a value in the List monad, yet it cannot be obtained by fmap
ing the input.
A major feature of a monad is to be able to "flatten" (join
). This is necessary to define a nice form of composition.
Consider the composition of two functions with side effects, say in the IO
monad:
foo :: A -> IO B
bar :: B -> IO C
If >>=
was only fmap
(without the final join
) pass, the composition
\a -> foo a >>= bar
would mean
\a -> fmap bar (foo a)
-- i.e.
fmap bar . foo
which does look as a nice composition, but unfortunately has type A -> IO (IO C)
! If we can't flatten the nested IO
, every composition would add yet another layer, and we would end up with result types of the form IO (IO (IO ...))
showing the number of compositions.
At best, this would be inconvenient. At worst, this would prevent recursion such as
loop = readLn >>= \x -> print x >>= \_ -> loop
since it leads to a type with an infinite nesting of IO
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With