inessential by Brent Simmons

Swift question: Enum vs. Struct for Variants

In a previous post I mentioned using structs plus a protocol to implement a variant type. There’s a Value protocol and a number of different structs that conform to that protocol.

But Andy Matuschak’s comment about using an enum instead keeps coming to mind. And then there’s this note in the pre-release Swift book:

Alternatively, enumeration members can specify associated values of any type to be stored along with each different member value, much as unions or variants do in other languages.

So let’s imagine a variant that can represent a bunch of different data types and that has logic for arithmetic operations and for coercions.

enum Value {
  case variantBool(Bool)
  case variantInt(Int)
  case variantString(String)
  etc. — for doubles, dates, data, collections, and so on
}

(Forgive the “variant” prefix in this example — it’s there so I’m not writing case Bool(Bool) and so on.)

Now let’s say we have a bunch of logic.

func canAddValue(value: Value) -> Bool
func valueByAddingValue​(value: Value) -> Value

And so on for subtracting, multiplying, and dividing. (Example: variantStrings can add and subtract other variantStrings and things that can be coerced to variantStrings, but they can’t multiply or divide. variantData can’t do any arithmetic at all. Etc.)

And then add logic for coercions:

func canCoerceToType​(type: Value) -> Bool
func valueByCoercingToType​(type: Value) -> Value

(This is a little weird because a Value is a type — variantBool, variantInt, etc. — as well as data.)

There’s a whole ton of logic there: a variantDate can be coerced to a variantDouble but not to a variantArray, etc.

Unless I’m missing something — which is highly likely, as I’m just barely starting — I’ve got a whole ton of switch statement to write. And those switch statements have inner switch statements. (Conceptually, that is. Of course I would break things out into separate functions, as in func variantBool​CanCoerceToType​(type: Value) -> Bool and so on.)

That seems a lot to me like writing C code with tagged unions. Which is not necessarily a bad thing — I grew up on C and and still love programming in C (though I don’t get much chance to do it these days).

My original idea — a Value protocol plus a bunch of structs conforming to Value, one for each type — is something an Objective-C developer who’s new to Swift would come up with.

I realize it’s still early to ask about idiomatic Swift and best practices, but I’m asking anyway. What would you do?