In this article, we’ll talk about Result and Maybe in Elm.

A close-up of a race track with an Elm logo overlaid

Note: Examples in this article use Elm 0.19.

Importing packages in Ellie-app and in Elm REPL

Here’s an example of an attempt at import that doesn’t work on Ellie.

The example comes straight from the docs, https://guide.elm-lang.org/effects/time.html, so that might confuse you.

The solution is actually pretty simple: You need to install the elm/time package in your Ellie. Click on the package icon in Ellie (second from the top on the left), type elm/time in the search box, and then click the INSTALL button on the appropriate result. Then recompile. This is conceptually similar to what you’d do on the command line if you weren’t using Ellie: elm install elm/time.

Working with Result in Elm 0.19

The official documentation for Result is available here. We use Result whenever there are things that our code does that can potentially fail. Using Result allows us to deal better with errors in Elm. The Result definition says that it’s a union type that has two type constructors: Ok and Err. Take a look at the following code snippet:

type Result error value
    = Ok value
    | Err error

If an operation succeeds, the Result is Ok. Otherwise, the Result is Err.

To test drive Result, all that we need to do is run an operation that might fail. REPL is the perfect place for such a test. Thus, let’s point our browser to http://elmrepl.cuberoot.in/ and run the following:

import Date
Date.fromString "2019-02-18"

Running the preceding in the REPL, will return the following:

Ok <Sun Feb 18 2019 00:00:00 GMT+0000 (UTC)> : Result.Result String Date.Date

Wonderful, we got back an Ok result from an operation that could have failed. Let’s try it again:

Date.fromString "0"

You might think that the preceding would fail, but it is actually completely valid:

Ok <Sat Jan 01 2000 00:00:00 GMT+0000 (UTC)> : Result.Result String Date.Date

Great, we still got back an Ok. This time, let’s use a string of letters, to make sure that the REPL can’t handle it:

Date.fromString "abc"

And indeed, the REPL doesn’t know how to deal with this input. However, instead of throwing an ugly exception, we get an elegant one:

Err "Unable to parse 'abc' as a date. Dates must be in the ISO 8601 format."
    : Result.Result String Date.Date

Above, we just ran an expression that could potentially fail, that is, we ran the Date.fromString function. The Date.fromString function takes a parameter of type String, and returns a Date. In the preceding code snippet, we gave it the String with the value of abc, and it returned an Err as the Result.

Note that Result has its own module, and that is why the type definition reads Result.Result.

Working with Maybe

Also a union type, Maybe is a way to deal with nothingness in Elm, or, put differently, with values that are optional, that is, which might or might not exist. The official documentation is available here, and it gives the definition for the Maybe union type as follows:

type Maybe a
  = Just a
  | Nothing

Using the letter a in Elm is a convention, and it’s a way to describe that the value used at that position can be anything. So the a in the preceding definition can be a String, an Int, or any other value.

So, in Elm, Maybe can be only one of two things— Just anything, or Nothing. More specifically, Maybe can be either one of these:

  • Just a String, or Just an Int, or Just a Float…, or
  • Nothing

Let’s now turn to Elm REPL at http://elmrepl.cuberoot.in/ and see the Maybe type in practice:

testingMaybe = Just 1

The REPL will return the following:

Just 1 : Maybe.Maybe number

As we can see, Just 1 is a Maybe number. Let’s do another one:

testingMaybe = Just 1.1

Running the preceding line will result in this:

Just 1.1 : Maybe.Maybe Float

Finally, let’s see a Nothing in action:

testingMaybe = Nothing

REPL replies with the following:

Nothing : Maybe.Maybe a

Since Nothing has no value, we are back to Maybe a. Elm guarantees require us to have an a here, even though Nothing is Nothing, and since it represents the absence of value it does not need a type for its value.

Destructuring a Maybe

To destructure a Maybe, we can use the case-of expression. Let’s see an example of that using Ellie:

module Main exposing (main)

import Html exposing (Html, text)


-- A person, but maybe we do not know their age.
type alias Person =
    { name : String
    , age : Maybe String
    }
tom = { name = "Tom", age = Just "42" }
sue = { name = "Sue", age = Nothing }


main =
    text <| 
    case tom.age of 
        Nothing ->
            "No age for this person"
        Just val ->
            val

In the preceding example, we are using a slightly altered version of the person example available at the official documentation for Maybe. The difference in the preceding code is that instead of an Int, we are using a String for the age entry in our record. That way, we can avoid making our code any more complex than it needs to be, and we still return a String from all the branches in our case-of expression.

The app will print out 42 upon compilation. Now, let’s change the app so that we are attempting to print Sue’s age. The only difference to be made is listed as follows:

case sue.age of

This time, upon compilation, the app will pattern-match the Nothing branch of the case-of expression in main, and that will result in the “No age for this person” message printed on the screen.

Here’s the app:

Result and Maybe with Defaults

Result and Maybe are quite similar. If our operation succeeds, we get an Ok a with Result. When dealing with Maybe, we get Just a if a value is present.

In case there was an error, for Result we get Err error. In case of no value in Maybe, we get Nothing.

Let’s now see what Result.withDefault does, by running the following in Elm REPL:

Result.withDefault 0 (Ok 1)

The REPL responds with the following:

1 : number

Now let’s try using Strings:

Result.withDefault "This is default." (Ok "This is OK")

The REPL returns the following:

"This is OK" : String

Similar to Result, we can also use withDefault on Maybe. Run the following in the REPL:

Maybe.withDefault 0 Nothing

The REPL returns the following:

0 : number

Let’s do a couple more. First, let’s give it a default String:

Maybe.withDefault "abc" Nothing

The REPL returns the following:

"abc" : String

How about a default Record?

Maybe.withDefault {} Nothing

Running the preceding code in the REPL will produce the following:

{} : {}

That’s it for this article. In the next one, we’ll make our todo editable.