3.12

Case Expression

Let’s say we have the weekdays being represented in numbers between 0 and 6 and we want to convert those numbers to strings (0 = Sunday, 1 = Monday, etc.). We can use an if expression to do that. Add the following function definition right above main in Playground.elm.

weekday dayInNumber =
    if dayInNumber == 0 then
        "Sunday"

    else if dayInNumber == 1 then
        "Monday"

    else if dayInNumber == 2 then
        "Tuesday"

    else if dayInNumber == 3 then
        "Wednesday"

    else if dayInNumber == 4 then
        "Thursday"

    else if dayInNumber == 5 then
        "Friday"

    else if dayInNumber == 6 then
        "Saturday"

    else
        "Unknown day"

Now let’s render Friday on a browser by calling the weekday function from main.

main =
    Html.text <| weekday 5

Refresh the page at http://localhost:8000/src/Playground.elm and you should see Friday. As it stands right now, the weekday function looks a bit superfluous. It does nothing more than a number comparison in each conditional branch. Can we express it more succinctly? Let’s try using a case expression.

weekday dayInNumber =
    case dayInNumber of
        0 ->
            "Sunday"

        1 ->
            "Monday"

        2 ->
            "Tuesday"

        3 ->
            "Wednesday"

        4 ->
            "Thursday"

        5 ->
            "Friday"

        6 ->
            "Saturday"

        _ ->
            "Unknown day"

Ah! Much better!

Case Expression Syntax

case works by matching an expression to a pattern. When a match is found, it evaluates the expression to the right of -> and returns whatever value is produced.

Pattern Matching
Pattern matching is the act of checking one or more inputs against a pre-defined pattern and seeing if they match. We will explore pattern matching in much more detail in chapter 4.

The expression after the keyword case can be anything from a simple value to a complex computation including function applications. For example, if we want to generate a hashtag for each day, we can write another function that takes a string value returned by the weekday function and matches it against a hashtag. Add the following function definition right above main in Playground.elm.

hashtag dayInNumber =
    case weekday dayInNumber of
        "Sunday" ->
            "#SinDay"

        "Monday" ->
            "#MondayBlues"

        "Tuesday" ->
            "#TakeMeBackTuesday"

        "Wednesday" ->
            "#HumpDay"

        "Thursday" ->
            "#ThrowbackThursday"

        "Friday" ->
            "#FlashbackFriday"

        "Saturday" ->
            "#Caturday"

        _ ->
            "#Whatever"

Now call the hashtag function instead of weekday from main.

main =
    Html.text <| hashtag 5

Refresh the page at http://localhost:8000/src/Playground.elm and you should see #FlashbackFriday. You might be tempted to line each arrow (->) up when writing a case expression like this:

hashtag dayInNumber =
    case weekday dayInNumber of
        "Sunday"    -> "#SinDay"
        "Monday"    -> "#MondayBlues"
        "Tuesday"   -> "#TakeMeBackTuesday"
        "Wednesday" -> "#HumpDay"
        "Thursday"  -> "#ThrowbackThursday"
        "Friday"    -> "#FlashbackFriday"
        "Saturday"  -> "#Caturday"
        _           -> "#Whatever"

The Elm style guide tells us not to do that. Although this arrangement is easier on the eye, it takes aways the ease of modification. Let’s say we need to add another day with a long name to the list of patterns. Now we need to move all the arrows. But if we arrange the code as suggested by the style guide, then it becomes much easier to modify. Although elm-format automatically reformats our code to follow the style guide, I wanted to point it out here so that you understand why a case expression in Elm is formatted like that.

Catch All Pattern

Notice how we used _ in both examples above as the last pattern? Without it, Elm will throw an error. Go ahead and remove the line that contains _ from the weekday function and refresh the page at http://localhost:8000/src/Playground.elm. You should see the following error.

We have to account for every single value an expression can have. Since dayInNumber holds an integer whose range is quite big, it’s not feasible to list all those options in our code. The _ character works as a catch-all-pattern. Because of this, it’s important to place the most specific pattern at the top and the least specific at the bottom. For example, if we move _ to the top, the weekday function will always return "Unknown day" no matter what number we pass in. Add the catch-all-pattern back to the weekday function.

weekday dayInNumber =
    case dayInNumber of
        .
        .
        _ ->
            "Unknown day"

If vs Case Expression

Does this mean all if expressions can be replaced with case? Not necessarily. A case expression works by matching a pattern, whereas an if expression checks whether a condition is true or not. For example, the following code doesn’t really have a pattern.

escapeEarth myVelocity mySpeed =
    if myVelocity > 11.186 then
        "Godspeed"

    else if mySpeed == 7.67 then
        "Stay in orbit"

    else
        "Come back"

If we attempt to re-write it with a case expression, we will get an error. Add the following function definition right above main in Playground.elm.

escapeEarthWithCase myVelocity mySpeed =
    case (myVelocity, mySpeed) of
        (myVelocity > 11.186) ->
            "Godspeed"

        (mySpeed == 7.67) ->
            "Stay in orbit"

        _ ->
            "Come back"

If you refresh the page at http://localhost:8000/src/Playground.elm, you should see the following error.

The reason why Elm is throwing an error is because there is no pattern to match here. All we have is a couple of disparate conditions. More importantly, the part before -> has to be a pattern. It can’t be any expression. Here we are trying to compare the given velocity and speed with some number using an expression.

Go ahead and remove the escapeEarthWithCase function from Playground.elm and refresh the page at http://localhost:8000/src/Playground.elm to make sure there are no errors.

To summarize, if we have disparate conditions to check then if expression is a good fit. Whereas if we need to match an expression to a pattern, a case expression is the way to go.

Back to top
Close