Elm Maybe

Handling problems explicitely

Opposed to JavaScript and many other programming languages, Elm does not have the concept of undefined or null values. Of course there are cases in which variables contain nothing or functions cannot return a proper value – but instead of leaving it up to you whether or not to handle these cases, Elm makes them explicit with a type called Maybe and enforces the handling thereof.

The Maybe module contains the type as well as some helper functions for dealing with optional arguments, error handling and records with optional fields. Maybe itself is a union type type with the two constructors Just and Nothing:

type Maybe value =
    Just value |
    Nothing

It is best to think of Maybe as a container for a single value, which can either be present or missing. For example, consider accessing elements of a List or an Array: Whereas in dynamic languages like JavaScript you would get undefined for values that do not exist, statically typed languages like Elm require a special type for representing the concept of nothingness.

import Array exposing (fromList)


myList =
    [ 1, 2, 3 ]


myArray =
    (fromList myList)


List.head myList
-- Just 1 : Maybe.Maybe number

Array.get 4 myArray
-- Nothing : Maybe.Maybe number

List.head gives back the first element of the given list, which may or may not exist. In this case the returned Just value wraps the actual value of 1 as the list has at least one element. When trying to access the forth element of the array we get back the value Nothing as the array contains only three elements.

This forces us to always handle both cases when trying to access values that may or may not exist: The compiler simply won’t compile our code if we have not covered both possibilities. This is actually a very good thing as the compiler assures you do not forget to handle errors and edge cases that will surely come up one day. It is also an example of how the compiler prevents runtime errors …

Dealing with Maybes

Ok, now that we are assured that we won’t miss any cases how do we handle Maybe values? Instead of using if and else as in JavaScript, in Elm we are using the case expression:

alice =
    Just ({ login = "alice" })


bob =
    Nothing


currentUserLogin : Maybe -> String
currentUserLogin user =
    case user of
        Just currentUser ->
          currentUser.login
        Nothing ->
          ""


currentUserLogin alice
-- "alice" : String

currentUserLogin bob
-- "" : String

Here we are creating the function alice returning a Maybe value by using the Just function with a sample record representing a user that is signed in. The function bob represents a signed out user by returning the value Nothing.

The user value is handled in the case statement contained in the currentUserLogin function: If it is Nothing the case statement returns an empty string. In case the user is signed in the statement destructures the Just value to currentUser and returns the users login.

Destructuring is a really nice feature in Elm and case statements can destructure all kinds of data structures. You will find it in many places in Elm code: Update functions use destructuring for Msg types and the Result and Task types are handled very similar to how we deal with Maybe.

More ways to handle Maybes

We can also access Maybe values by mapping them or using the functions withDefault and oneOf:

Maybe.map (\n -> n + 2) (Just 1)
-- Just 3 : Maybe.Maybe number

Maybe.map (\n -> n + 2) Nothing
-- Nothing : Maybe.Maybe number

Maybe.withDefault 3 (Just 5)
-- 5 : number

Maybe.withDefault 3 Nothing
-- 3 : number

Maybe.oneOf [ Nothing, (Just 1), (Just 2) ]
-- Just 1 : Maybe.Maybe number

Maybe.oneOf [ Nothing, Nothing, Nothing ]
-- Nothing : Maybe.Maybe a

And last but not least the higher order function andThen can be used to chain computations. It receives a Maybe value and a callback function that gets invoked in case there is a value:

(List.head [3,4,5]) |> Maybe.andThen (\n -> Just(n * 3))
-- Just 9 : Maybe.Maybe number

Here List.head returns Just(3) which is then passed to an anonymous function. This function multiplies it with 3 and returns the value 9 wrapped in a Just which could then be passed to another andThen as well.

TL;DR The Maybe module allows us to model and handle cases of non-existing values explicitely. The compiler forces us to handle each of the possible cases and guarantees there will be no runtime errors due to empty values.