Result

A Result is useful when you have logic that may “fail”. For example, parsing a String into an Int may fail. What if the string is filled with the letter B? In cases like this, we want a function with this type:

  1. String.toInt : String -> Result String Int

This means that String.toInt will take in a string value and start processing the string. If it cannot be turned into an integer, we provide a String error message. If it can be turned into an integer, we return that Int. So the Result String Int type is saying, “my errors are strings and my successes are integers.”

To make this as concrete as possible, let’s see the actual definition of Result. It is actually pretty similar to the Maybe type, but it has two type variables:

  1. type Result error value
  2. = Err error
  3. | Ok value

You have two constructors: Err to tag errors and Ok to tag successes. Let’s see what happens when we actually use String.toInt in the REPL:

  1. > import String
  2. > String.toInt "128"
  3. Ok 128 : Result String Int
  4. > String.toInt "64"
  5. Ok 64 : Result String Int
  6. > String.toInt "BBBB"
  7. Err "could not convert string 'BBBB' to an Int" : Result String Int

So instead of throwing an exception like in most languages, we return data that indicates whether things have gone well or not. Let’s imagine someone is typing their age into a text field and we want to show a validation message:

  1. view : String -> Html msg
  2. view userInputAge =
  3. case String.toInt userInputAge of
  4. Err msg ->
  5. span [class "error"] [text msg]
  6. Ok age ->
  7. if age < 0 then
  8. span [class "error"] [text "I bet you are older than that!"]
  9. else if age > 140 then
  10. span [class "error"] [text "Seems unlikely..."]
  11. else
  12. text "OK!"

Again, we have to use case so we are guaranteed to handle the special case where the number is bad.