In my pre­vi­ous post on dis­crim­i­nated unions, I pre­sented dis­crim­i­nated unions as an alter­na­tive to stan­dard .Net classes to rep­re­sent hier­ar­chi­cal data struc­tures. How­ever, in terms of data struc­ture, dis­crim­i­nated unions share much more sim­i­lar­i­ties with enums than they do classes – both allow you to define a set of named con­stants and asso­ciate some data with these constants.

The syn­taxes for cre­at­ing enums and dis­crim­i­nated unions in F# are very sim­i­lar too:

image

Despite their appar­ent sim­i­lar­i­ties, there are some sig­nif­i­cant dif­fer­ences between the two:

    • Enums don’t offer a safety guarantee
    • Enums only hold one piece of data
    • Dis­crim­i­nated unions are ref­er­ence types
    • Enums can be used as bit flags

Now let’s take a closer look at these differences.

Enums don’t offer a safety guarantee

As enums are lit­tle more than syn­tac­tic sugar over a prim­i­tive inte­gral type such as int, there is no guar­an­tee that the value of an enum is valid. For instance, it’s pos­si­ble to cre­ate an instance of an enum type with an inte­gral value that is not asso­ci­ated with one of the named constants:

image

it’s easy to see how bugs can creep in when you mis­tak­enly cre­ate enum val­ues that don’t make any sense, espe­cially when you’re work­ing with enum val­ues from exter­nal sources. Which is why it’s a good prac­tice to check the enum val­ues with the sta­tic Enum.IsDefined method.

Dis­crim­i­nated unions, on the other hand, can only be one of the defined val­ues, any attempts to do oth­er­wise will be met with a swift com­piler error!

Enums only hold one piece of data

This one is self evi­dent from the ear­lier snip­pet, enums only hold one piece of data but dis­crim­i­nated unions hold a tuple of data.

Dis­crim­i­nated unions are ref­er­ence types

Enums are value types and instances of an enum type there­fore reside on the stack as a few bytes. Dis­crim­i­nated unions, as do all other ref­er­ence types, reside in the heap (plus a pointer on the stack whilst it’s still ref­er­enced) and need to be garbage col­lected when they are no longer referenced.

The impli­ca­tion of this is such that enums offer sig­nif­i­cant per­for­mance ben­e­fits over dis­crim­i­nated unions. Take the fol­low­ing snip­pet for instance, where I pop­u­late two arrays with 10 mil­lion items, one with enums and the other dis­crim­i­nated unions.

image

Aver­aged over three runs, the enum array took 0.048 sec­onds to fin­ish whilst the dis­crim­i­nated union array took 1.919 sec­onds!

Enums can be used as bit flags

From MSDN:

You can use an enu­mer­a­tion type to define bit flags, which enables an instance of the enu­mer­a­tion type to store any com­bi­na­tion of the val­ues that are defined in the enu­mer­a­tor list.

image

Share

2 Responses to “F# — Enums vs Discriminated Unions”

  1. Wuxab says:

    Should it not be
    OptionA = 1
    OptionB = 2
    OptionC = 4
    OptionC = 8

    Or is it auto­mat­i­cally 2^x for enums?

  2. theburningmonk says:

    Wuxab — you’re right, well spot­ted, com­pletely missed that! I’ve updated the last exam­ple accordingly.

Leave a Reply