Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell >>= operation: why is the function argument required to return another Monad?

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.

like image 735
northlane Avatar asked Nov 15 '17 21:11

northlane


People also ask

What is Haskell monad function?

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.

How does return work in Haskell?

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.

What does the bind operator do Haskell?

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.

What are monads explain with example?

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.


2 Answers

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 joining 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 fmaping the input.

like image 97
Fyodor Soikin Avatar answered Sep 24 '22 03:09

Fyodor Soikin


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.

like image 44
chi Avatar answered Sep 25 '22 03:09

chi