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.