Pattern Matching

On the previous page, we learned how to create custom types with the type keyword. Our primary example was a User in a chat room:

  1. type User
  2. = Regular String Int
  3. | Visitor String

Regulars have a name and age, whereas visitors only have a name. So we have our custom type, but how do we actually use it?

case

Say we want a toName function that decides on a name to show for each User. We need to use a case expression:

  1. toName : User -> String
  2. toName user =
  3. case user of
  4. Regular name age ->
  5. name
  6. Visitor name ->
  7. name
  8. -- toName (Regular "Thomas" 44) == "Thomas"
  9. -- toName (Visitor "kate95") == "kate95"

The case expression allows us to branch based on which variant we happen to see, so whether we see Thomas or Kate, we always know how to show their name.

And if we try invalid arguments like toName (Visitar "kate95") or toName Anonymous, the compiler tells us about it immediately. This means many simple mistakes can be fixed in seconds, rather than making it to users and costing a lot more time overall.

Wild Cards

The toName function we just defined works great, but notice that the age is not used in the implementation? When some of the associated data is unused, it is common to use a “wild card” instead of giving it a name:

  1. toName : User -> String
  2. toName user =
  3. case user of
  4. Regular name _ ->
  5. name
  6. Visitor name ->
  7. name

The _ acknowledges the data there, but also saying explicitly that nobody is using it.