Error Handling

One of the guarantees of Elm is that you will not see runtime errors in practice. This is partly because Elm treats errors as data. Rather than crashing, we model the possibility of failure explicitly with custom types. For example, say you want to turn user input into an age. You might create a custom type like this:

  1. type MaybeAge
  2. = Age Int
  3. | InvalidInput
  4. toAge : String -> MaybeAge
  5. toAge userInput =
  6. ...
  7. -- toAge "24" == Age 24
  8. -- toAge "99" == Age 99
  9. -- toAge "ZZ" == InvalidInput

Instead of crashing on bad input, we say explicitly that the result may be an Age 24 or an InvalidInput. No matter what input we get, we always produce one of these two variants. From there, we use pattern matching which will ensure that both possibilities are accounted for. No crashing!

This kind of thing comes up all the time! For example, maybe you want to turn a bunch of user input into a Post to share with others. But what happens if they forget to add a title? Or there is no content in the post? We could model all these problems explicitly:

  1. type MaybePost
  2. = Post { title : String, content : String }
  3. | NoTitle
  4. | NoContent
  5. toPost : String -> String -> MaybePost
  6. toPost title content =
  7. ...
  8. -- toPost "hi" "sup?" == Post { title = "hi", content = "sup?" }
  9. -- toPost "" "" == NoTitle
  10. -- toPost "hi" "" == NoContent

Instead of just saying that the input is invalid, we are describing each of the ways things might have gone wrong. If we have a viewPreview : MaybePost -> Html msg function to preview valid posts, now we can give more specific error messages in the preview area when something goes wrong!

These kinds of situations are extremely common. It is often valuable to create a custom type for your exact situation, but in some of the simpler cases, you can use an off-the-shelf type instead. So the rest of this chapter explores the Maybe and Result types, showing how they can help you treat errors as data!