Monads without talking about Monads

Lucamug
4 min readOct 8, 2018

--

With code examples in Elm

Functions

In Functional Programming we build programs only using functions. These functions, for simplicity, can return only one value.

Examples of functions:

mul3 x = 3 * x add2 x = 2 + xdiv ( x, y ) = x / y

The first function multiply x by 3, the second function add 2 to x and the third function accept a Tuple with two values x and y and return x divided by y.

Composition

How can we describe the order of execution of these functions to transform this list in a program? We compose them. For example.

result ( x, y ) = add2 (mul3 (div ( x, y )))

or, using another possible form in Elm, that I will use from now on:

result ( x, y ) =
( x, y )
|> div
|> mul3
|> add2

We need functions and functions composition, so that we can structure our program. Composition is the essence of software development, it helps us to handle complexity.

Failures

Sometime functions can fail, how can we deal with such situation?

For example, dividing by zero is a failing situation for div because it would’t return any number but the function must always return the same type and the type signature of div is:

div: ( Float, Float ) -> Float

Throwing an exception is not allowed in pure functions so let’s try building a type that could encapsulate the idea of failure, creating a box that can contain two possible values, just “a” or Nothing:

type Maybe a = Just a | Nothing

such type generate automatically two functions with this type signature:

Just : a -> Maybe aNothing : Maybe a

Now we can rewrite our function as

divM ( x, y ) =
if y == 0 then
Nothing
else
Just <| x / y

Note: I added “M” to the name of the function to highlight the fact that it returns a Maybe value.

Composition II

Unfortunately now we would not be able to compose our functions because

result ( x, y ) =
( x, y )
|> divM
|> mul3
|> add2

would generate this compiling error

This `divM` call produces:    Maybe FloatBut `mul3` needs the 1st argument to be:    Float

But, as explained earlier, composition is important and we want to keep it. The question now is: how can we easily concatenate functions that may fail?
We could rewrite them:

mul3MM maybe =
case maybe of
Just x ->
Just <| 3 * x
Nothing ->
Nothing
add2MM maybe =
case maybe of
Just x ->
Just <| 2 + x
Nothing ->
Nothing

Note: I added “MM” to the name of the functions to highlight the fact that they accept a Maybe value and return a Maybe value.
Ok, now the composition works again:

result ( x, y ) =
( x, y )
|> divM
|> mul3MM
|> add2MM

We are able to compose function that can fail. As soon as one function fail, the chain will stop and Nothing is returned.

It seems that there is some duplicated code in mul3MM and add2MM. Can we try to be more DRY?

Binding

What is about creating a function like this?

andThen callback maybe =
case maybe of
Just value ->
callback value
Nothing ->
Nothing

now we can remove the case from the functions above. They became:

mul3M x = Just <| 3 * xadd2M x = Just <| 2 + x

We left the embellishment (Just…)

Now we rewrite the above functions using andThen

mul3MM = andThen mul3Madd2MM = andThen mul3M

We can bring now these functions into the composition:

result ( x, y ) =
( x, y )
|> divM
|> andThen mul3M
|> andThen add2M

The andThen function is helping in chaining together these new functions. It is the analogue of bind (>>=) in Haskell, in the Maybe realm, just with the two input parameters inverted:

# Haskell(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
(>>=) maybe callback =
case maybe of
Just value ->
callback value
Nothing ->
Nothing
# ElmandThen : (a -> Maybe b) -> Maybe a -> Maybe b
andThen callback maybe =
case maybe of
Just value ->
callback value

Nothing ->
Nothing

The Maybe.andthen function is built in the core of Elm together with Result.andThen and Task.andThen.

Result is similar to Maybe but instead of Nothing, it has Err error that carry extra data about the error details, while Ok value is the equivalent of Just a:

type Result error value = Ok value | Err error

Conclusion

The binding function andThen is a way to add procedural behaviour in Functional Programming.

With a simple modifications to our functions (from 3 * x to Just <| 3 * x, for example), we are now able to compose them in a way that if a function fail, we short-circuit the entire flow and immediately go to the end of the computation.

This is monad composition, a very powerful concept in Functional Programming.

--

--

No responses yet