United Kingdom: +44 (0)208 088 8978

F# 9 improves DX for Discriminated Unions

Simplifying case checks with F# 9's new auto-generated DU utilities.

We're hiring Software Developers

Click here to find out more

This post is part of our series about F# 9. Check out the full series overview for more posts about shiny new F# features!

F# 9 introduces quality-of-life improvements to how developers interact with Discriminated Unions (DUs). As DUs are fundamental to F# domain modeling, these enhancements make working with them even more intuitive.

The most notable improvement is the auto-generation of .Is* utility functions for each DU case. These functions act as a membership check, making it simple to determine which case is currently assigned to a DU.

Let's look at an example:

type Contact =
    | Email of address: string
    | Phone of countryCode: int * number: string
type Person = { name: string; contact: Contact }

Here we have a DU Contact which can be either an Email (containing an address) or a Phone (consisting of a country code and number). We also create a Person type which has a record property contact as the Contact DU.

While pattern matching remains the primary way to "unwrap" DUs and handle cases explicitly, there are scenarios where simpler approaches make sense. One common pattern across F# codebases is checking which case a DU contains - something that previously required verbose pattern matching:

let canSendEmailTo person =
    match person.contact with
    | Email _ -> true
    | _ -> false

With F# 9, this becomes much more concise:

let canSendEmailTo person =
    person.contact.IsEmail    

This improvement streamlines code where you simply need to check a DU's case. The new syntax is not only more readable but requires less boilerplate code. Pattern matching remains ideal for sophisticated logic - its explicit nature makes complex case handling clear and maintainable. However, for simple membership checks, the new .Is utility provides a cleaner alternative.

These improvements in F# 9 demonstrate the language's continued evolution in making domain modeling more ergonomic while maintaining its functional programming principles. We're excited to leverage these new language features in our systems and look forward to seeing how they improve our codebase's clarity and maintainability.