Named Values: The Discriminated Union Example that I Rarely if Ever See

Two things really attracted me to F# originally: type providers and discriminated unions. As I started working with the language I scoured the Web for all of the content I could find.  It wasn’t quite as plentiful as it is now but both F# for Fun and Profit and TryFSharp existed in some form before I began exploring in earnest so that was incredibly helpful. I’ve noticed a certain amount of commonality between the various presentations of the discriminated union across the internet.

We often see examples that employ shapes or cards or int*bool unions.  Those show off terse and powerful nature of the feature very well.  When discriminated unions grow beyond a small number of values, though, they can become headache-inducing very quickly. With a small number of values of different types involved, those discriminated unions can be memorized or thought through because when we’ve got a Person of string * DateTime * int we’re not going to mix up the name, date of birth, and SAT score or whatever because the other data types don’t make sense. In cases where there’s an accepted convention, like with geographic locations or three-dimensional coordinates, the fact that the unions contain the same type multiple times isn’t a problem because we can assume that the order of the values is latitude and longitude or x, y, and z. If developers follow those conventions consistently throughout the code, anyway.

Where neither of these cases hold, I used to think that my options ranged from “make liberal use of comments” to “don’t use discriminated unions”. Casual Googling fails to turn up any documentation, but I stumbled on a third option that I’m embarrassed that I didn’t think of immediately after writing “Hello World” and am quietly amazed hasn’t gotten more press: just name the values.

type Person =
| Student of string * DateTime * string * int*int*int*int*floatDiscriminated Union-Student

type PersonWithNamedValues =
| Student of Name:string * BirthDate:DateTime * Major:string * GraduatingYear:int*SAT:int*ACT:int*ClassRank:int*GPA:floatDiscriminated Union-StudentWithNames

It’s a small difference, and of course when we consume one of these types in a match expression we can assign arbitrary names to each of the values so there’s nothing world-changing about having sensible names from the outset, but there’s a certain amount of payoff during the declaration.

Call it nitpicky, but I tend to think that anything we can do that we can make our code more readable and easier for new people to understand quickly will be worth the time we invest, particularly if it helps us immediately as we’re writing it.