Question

I've just noticed that there's only a little difference in declaring a non-member discriminated union:

type Color =
    | Red
    | Green
    | Blue

and declaring an enum:

type Color =
    | Red = 0 
    | Green = 1
    | Blue = 2

What are their main differences in terms of performance, usage, etc? Do you have suggestions when to use what?

Was it helpful?

Solution

Enum are stucts and are therefore allocated on the stack, while discriminated unions are references types so are heap allocated. So, you would expect DU to be slightly less performant that enums, though in reality you'll probably never notice this difference.

More importantly a discriminated union can only ever be one of the types declared, where as enums are really just an integer, so you could cast an integer that isn't a member of the enum to the enum type. This means that when pattern matching the compiler can assert that the pattern matching is complete when you've covered all the cases for a DU, but for an enum you must always put in a default catch all the rest case, i.e for an enum you'll always need pattern matching like:

match enumColor with
| Red -> 1 
| Green -> 2
| Blue -> 3
| _ -> failwith "not an enum member"

where as the last case would not be necessary with an DU.

One final point, as enums are natively supported in both C# and VB.NET, were as DUs are not, enums are often a better choice when creating a public API for consumption by other languages.

OTHER TIPS

In addition to what Robert has said, pattern matching on unions is done in one of two ways. For unions with only nullary cases, i.e., cases without an associated value (this corresponds closely to enums), the compiler-generated Tag property is checked, which is an int. In this case you can expect performance to be the same as with enums. For unions having non-nullary cases, a type test is used, which I assume is also pretty fast. As Robert said, if there is a performance discrepancy it's negligible. But in the former case it should be exactly the same.

Regarding the inherent "incompleteness" of enums, when a pattern match fails what you really want to know is if a valid case wasn't covered by the match. You don't generally care if an invalid integer value was casted to the enum. In that case you want the match to fail. I almost always prefer unions, but when I have to use enums (usually for interoperability), inside the obligatory wildcard case I pass the unmatched value to a function that distinguishes between valid and invalid values and raises the appropriate error.

As of F# 4.1 there are struct discriminated unions.

These have the performance benefits of stack allocation, like enums.

They have the superior matching of discriminated unions.

They are F# specific so if you need to be understood by other .Net languages you should still use enums.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top