Items in tuples don’t have names, which means that you often don’t have a clear way to document the meanings of each item.
For instance, in this discriminated union:
type NetworkEvent =
| Message of string * string * string
| ...
I want to make it clear that the first and second items are the sender and recipient names respectively. Is it good practice to do something like this:
type SenderName = string
type RecipientName = string
type NetworkEvent =
| Message of SenderName * RecipientName * string
| ...
A lot of C/C++ libraries have a proliferation of types (e.g. win32.h), but in those languages, even though parameter names are optional in many cases, it can still be done. That isn’t the case with F#.
I think that using type aliases for documentation purposes is a good and simple way to document your discriminated unions. I use the same approach in many of my demos (see for example this one) and I know that some people use it in production applications too. I think there are two ways to make the definition more self-explanatory:
Use type aliases: This way, you add some documentation that is visible in the IntelliSense, but it doesn’t propagate through the type system – when you used a value of the aliased type, the compiler will treat it as
string, so you don’t see the additional documentation everywhere.Use single-case unions This is a pattern that has been used in some places of the F# compiler. It makes the information more visible than using type-aliases, because a type
SenderNameis actually a different type thanstring(on the other hand, this may have some small performance penalty):Use records: This way, you explicitly define a record to carry the information of a union case. This is more syntactically verbose, but it probably adds the additional information in the most accessible way. You can still use pattern matching on records, or you can use dot notation to access elements. It is also easier to add new fields during the development: