Functional programming — Maybe and Either

Kevin Da Silva
4 min readDec 9, 2021

--

We'll keep talking in this article about more functional programming concepts like In the previous ones. We’ve already covered the reasons functional programming is so in the hype, function concepts, control flow, and today we are going to cover:

Maybe

Maybe is a data type that wraps other types and helps us to avoid the errors of applying computations in a previously failed computation, like a null, Nan, or undefined value.

In general functional languages don't have the concept of Nan, null or undefined because almost all of these errors and exceptions are mitigated by the Maybe data type.

The Maybe type is composed of two variants (like boolean with true and false), but in Maybe we have the Nothing as our failure context and Just as a wrapper for our successful computation.

Now let's see some maybes in action:

safeDiv v 0 = Nothing
safeDiv v d = Just(v `div` d)
maybeFn Nothing = Nothing
maybeFn (Just x) = Just(show x ++ " is a valid value.")

First, we have a function called safeDiv that will perform a safe division and takes a value and a divisor if the divisor is zero we get an arithmetic error because in mathematics we don't have the concept of division by zero and instead of returning an exception, null or Nan, we return Nothing that is our failure context for the Maybe type.

If our divisor is not zero, then we perform the division and wrap it in a Just to mark this computation as a successful operation.

And below safeDiv we have the maybeFn function that is expecting a Maybe value if our maybe value is a Nothing we don't apply computations to a non-successful previous computation and only return the Nothing type.

If maybeFn receives a Just(successful computation) we take the successful computation(the x) from Just, turn x into a string and append the message saying that x is a successful value, once the append computation is done we wrap it again in a Just(appended string message).

Now if we call:

safeDiv 30 0 -- outputs Nothing
safeDiv 30 2 -- outputs Just 15
maybeFn Nothing -- outputs Nothing
maybeFn Just(15)-- outputs Just "15 is a valid value"
-- Now if we call maybeFn with the result of safeDiv:
maybeFn $ safeDiv 30 0 -- outputs Nothing
maybeFn $ safeDiv 30 2 -- outputs Just "15 is a valid value"

Just “amazing”!!!

Either

Photo by Benoit Debaix on Unsplash

Either is also a data-type and has many use cases in functional programming, eithers can be seen as a way to return two different types to a function(for example a function that returns a number or a boolean), a context of failure that keeps a tracing of what caused the failure in the computation, or as a very elegant, simple and concise exception handling without all the GO-TO try-catch blocks

Either, like the Maybe type, is also composed of two options the Left(value) and Right(value)

Simple Either function that returns two different types for the same function:

strOrBool True  = Left True
strOrBool False = Left False
strOrBool _ = strOrBool = Right "Not a boolean"

Either as a context of failure:

In Maybe, we have Nothing for a non-successful computation and a Just(value) for a correct computation, eithers as a context of failure work in the same way with our Left(error message tracing) or Right(successful computation).

safeDiv v 0 = Left "Division by zero failure"
safeDiv v d = Right(v `div` d)
safeDiv 30 0 --outputs Left "Division by zero failure"
safeDiv 30 2 --outputs Right 15

Very similar to the safeDiv function in the maybes but now using the Either data-type.

Now Either as a light in the middle of the GO TO try-catch block darkness, Either as exception handling:

updateComment (pipe, dbName) commentId content = do
updatedComments <- try (access pipe master dbName
(updateCommentsOperation commentId content))
:: IO (Either SomeException ())
case updatedComments of
Left _ -> return "Exception updating comment"
Right _ -> return "Commented successfully updated"

The example above is a bit more complex, so let's break it into small pieces:

updateComment (pipe, dbName) commentId content

A function that takes the database connection, a commentId, and new content and updates the comment by its commentId.

updatedComments <- try (access pipe master dbName
(updateCommentsOperation commentId content))

We call the try function and pass the call to the access function that accesses our database and call the updateCommentsOperation function to update the comment by the commentId and assign the result to the updatedComments variable.

:: IO (Either SomeException ())

It's the definition of the type of the final value we are expecting from the try function call, in this case, will be an IO(because we are accessing the database) and the result will be Either Left(SomeException) or Right ().

case updatedComments of
Left _ -> return "Exception updating comment"
Right _ -> return "Comment successfully updated"

And finally based on the result we get in the updatedComments we just do a simple case if it's a Left Exception we get “Exception updating comment” message, if it is a Right () we get “Comment successfully updated” message.

It's so nice to see that when we get familiar with eithers exception handling becomes so precise and idiomatic.

Whew! And here we are at the end of one more article, hope it was useful and clear to you, and thank you for reading until here. Bye Bye.

Just “The end”!

--

--

Kevin Da Silva

I'm a back-end developer, functional programming lover, fascinated by computer science and languages. From the south part of Brazil to the world