Safer Programming with F# — Pattern Matching

Ankit Solanki
ClearTax Engineering
3 min readNov 2, 2016

Originally published on the ClearTax blog, in November 2014.

At ClearTax, we recently built a new version of our TDS Return software. And, we built it using F#.

F# is a wonderful language. It’s a mixed-paradigm programming language, but it mainly tries to get you to program in a Functional manner.

This is a blog post dives into one of the useful features of the language — Pattern Matching [1] — and shows how you can use it to write simpler, safer code.

Pattern Matching is if…else or switch…case on steroids. Here’s a quick example:

/// Your traditional recursive fibonacci number function
let rec fibonacci nth =
match nth with
| 0 -> 1
| 1 -> 1
| n -> (fibonnaci (n - 1)) + (fibonnaci (n - 2))

In this case, pattern matching similar to writing a if…else condition in any other language. You could easily rewrite this in JavaScript as:

function fibonacci(nth) {
if (nth == 0) { return 1; }
else if (nth == 1) { return 1; }
else { return fibonacci(nth - 1) + fibonacci(nth - 2) }
}

Or using a switch…case. But pattern matching is so much more powerful than either of these constructs.

You can match on multiple values at once

match a , b with
| false , false -> "Both a and b are false"
| false , true -> "Only b is true"
| true , false -> "Only a is true"
| true , true -> "Both a and b are true"

This sort of approach lets you build up a decision table in a very quick and readable manner.

Pattern matching forces you to be exhaustive

If you miss a possible decision path, the compiler will raise a warning. This forces you take care of every eventuality, unlike switch.
(You do take care to ensure that all compiler warnings are taken care of, don’t you?)

You can specify custom conditionals inline

/// Trying to verify an email address before signup
let canRegisterUser =
match user.emailAddress with
| "" -> "Missing"
| email when not (isValid (email)) -> "Invalid"
| email when isRegistered (email) -> "Existing"
| email -> "Yes"

You can ignore parts of a match

Specifying an _ in the match is basically the same as allowing any value at that point.

match a , b with
| false , _ -> "A == false. We do not care what B is"
| _ , false -> "A == true and B == false"
| _ , true -> "A == true and B == true"

You can really flatten your code with pattern matching

Combining all these approaches, using Pattern matching can really flatten nested code and make it much more readable, and error free.

Instead of writing 3 different nested if conditions, you can do something like:

let status =
match isAuthenticated, isAuthorized, canEditItem with
| false , _ , _ -> "User is logged-out"
| _ , false , _ -> "User is not authorized to view item"
| _ , _ , false -> "This item cannot be edited"
| _ , _ , _ -> "User can proceed to edit"

With the proper alignment (as above), this becomes very readable. This technique reminds me of a demo of the Subtext language I had seen a while back: while this isn’t as powerful as what that talk shows, it’s still much better than writing a 3 level nested condition that requires mental gymnastics in order to understand.

Pattern really shines when you combine it with some other base features of the F# language: Tuples & Discriminated Unions.

If you’re interested, start reading more about F# online. F# for Fun and Profit is a really nice resource you can use to get started.

Other languages that support pattern matching include Erlang (and Elixr), Haskell, Racket, etc.

BTW, ClearTax is hiring! If you found this post interesting, and want to work with F#, give us a shout out! You can help build the future of financial software for India.

  1. Pattern matching is most powerful when combined with F#’s union data types, but we will be completely ignoring this for now, and see how it can help ‘plain old code’.

--

--

Ankit Solanki
ClearTax Engineering

Co-Founder of ClearTax.in [YCS14] — our mission is to simplify the financial lives of Indians. We’re hiring! https://cleartax.in/s/career