Union types vs enums

It feels like there is some overlap in use cases for union types and enums, and I’m wondering whether there already has evolved some pragmatic, hands-on rules of thumb for deciding which one to use for a given scenario…? One example is the use of union types representing Lisp types in this discussion. For another one, I’ve been looking into tag URIs and I’d be leaning towards representing the date component as

type DateSpec = Year | YearMonth | LocalDate

To my understanding, union types (in concert with other language features) can be used to emulate most aspects enums, except for inheritance and GADT aspects. A union type over some “wrapper” types like opaque types or single field case classes would make for a tagged union type. Using full-fledged case classes as product type constituents would give me the full ADT “shape”. Type classes and extensions work fine with union types, as well.

It feels like union types, as the more lightweight approach, might be the default. But then I wonder whether there’s a “boiling frogs” bias where one keeps throwing other language features at a union type beyond the point where an enum would’ve been more practical and concise.

A friend suggested that this resembles the choice between (anonymous/named) tuples and proper case classes, but I’m not 100% sure this nicely captures the essence, as the gap between runtime characteristics and typing effort is closer in this case than for union types vs enums.

Certainly not a crucial question, just wondering whether anybody has given this some thought already. :slightly_smiling_face:

1 Like

The main advantage of unions over enum is to avoid creating a boxing wrapper.
On the other hand, that means that unions won’t work unless they are fully disjoint.

Another reason to use enums other unions would be for better java interop (if that is a concern), since enums provide a type hierarchy.

A function that takes or produces an enum will just take the enum type. Whereas a union is most likely the LUB

My rule of thumb: use enum every time.

No seriously. Union types are for when you know exactly what you want. So by definition, if you’re asking yourself, enum it is.

5 Likes

One more aspect: there is no typeclass-derivation for unions. (There are workarounds, but they are rather messy.)

1 Like

Thanks a lot for the responses! Some of the aspects I had (vaguely) touched, e.g. I had boxing in mind when mentioning “runtime characteristics”, and Java interop might be filed under “inheritance”, others I hadn’t thought about at all.

My guts feeling is that @sjrd’s advice is good as a rule of thumb, but I’m still struggling to back it. Looking at my examples, I’d think the union type for the tag URI is fine - it’ll only ever be used for comparisons and rendering. (Having it extend Temporal would be somewhat nice, but that’d be significant effort with enum, too.) For the Lisp types, OTOH, I’d be tempted to go with enum - but I couldn’t really argue what precisely might backfire with the union type, which looks enticingly more lightweight in comparison. :thinking: