[swift-evolution] Working with enums by name

Leonardo Pessoa me at lmpessoa.com
Thu Jun 2 07:00:16 CDT 2016

I understand you don't like to rely on the name of your enum cases in you code and that's fine, you can still work with them as is, you won't have to change the way you work just because you don't like the proposal but that doesn't mean everybody has the same opinion you do. But perhaps then instead of enums we should go back to using simple constants because that's exactly what enum cases become if their names are not how you're expected to reference, store and retrieve their values (yes, I understand there are other benefits to using enums instead of constants but that's just how you're using them).


-----Original Message-----
From: "Brent Royal-Gordon" <brent at architechies.com>
Sent: ‎02/‎06/‎2016 01:40 AM
To: "Leonardo Pessoa" <me at lmpessoa.com>
Cc: "Vladimir.S" <svabox at gmail.com>; "swift-evolution" <swift-evolution at swift.org>
Subject: Re: [swift-evolution] Working with enums by name

> Brent, for needing "both Int and Double values" there is a proposal to add tuples instead of the current raw values or allowing assessor properties per case, you should check those out. Perhaps this could also be used to cryptoghaphically sign a raw value but I'm not sure.

I know; I was the one who suggested accessors.

> As for working with enum values by name a few examples have already been posted in today but I've done a lot more research in the subject along the day and found there is a correlation between enums and nominal level values in statistics; we cannot test them for a particular order (this could also be interesting for statistic apps but it's another case) and no math with them is valid. So, e.g., the result of the following operation on the planets enum is nonsense:
> |   let planet = Planet(rawValue: Planet.Mars.rawValue - Planet.Mercury.rawValue)
> The result will be different if the enum values are zero based than if not. Also any change in list order or the base index or add a new element to the middle of the list will break your intended code if you're storing the raw value in a database. And we know these changes happen.

All of this is true. And all of this is an argument that *you're using raw values wrong*.

Raw values are a serialization mechanism. You might be serializing merely within your process, or you might be writing out to disk, through IPC, or across the network, but in all cases you are serializing. You should not be doing arithmetic with a serialized representation (unless that arithmetic is a part of the serialization process, like an error-correcting code you're applying to it). You should not be sorting serialized representations. You should be either communicating the raw value or recreating the instance with it—nothing else.

In other words, all of these are arguments for putting the order of the Planet in a separate property rather than in the `rawValue`. This would free up the `rawValue` to be a `String` containing the case name. This is not an argument for having both an Int `rawValue` and a String `caseName`; this is an argument for having both a String `rawValue` and an Int property.

	enum Planet: String {
		accessor var order: Int
		case mercury { order = 0 }
		case venus { order = 1 }

(Want it to be automatic? One could imagine having a compiler substitution for "the index of this case" which could be used as the default value for an accessor:

	enum Planet: String {
		accessor var order: Int = #caseIndex
		case mercury, venus, ...

Or let you choose a base:

	enum Planet: String {
		accessor var order: Int = #caseOrder(from: 1)
		case mercury, venus, ...

Or the `ValuesEnumerable` proposal would give you a convenient, though slightly slow, way to do two-way lookup by order:

	enum Planet: String, ValuesEnumerable {
		var order: Int {
			return Planet.allValues.index(of: self)!
		init(order: Int) {
			self = Planet.allValues[order]
		case mercury, venus, …

In short, there are several plausible mechanisms to automate assignment of these numbers, should you want to do that.)

> Actually, given this characteristic of nominal types (statistic), we should vow to removing init(rawValue:) completely from the language.

That doesn't follow. The fact that changing something would break code doesn't mean that thing should be removed; it means you should be cautious about changing that thing. If you rename a case, its name changes; does that mean we shouldn't have case name lookups either? Changing any identifier could break something; maybe we should abolish all identifiers from Swift?

> The real value you're working with in enums is the enum case name not any associated values. By working with the name you're safe should any associated value change, should their order change, you'll only break your app if the case is removed/renamed (with the raw value, you risk having the wrong treatment being given should another enum case takes the value of a removed one).

RawRepresentable is a serialization mechanism, so *of course* changing the raw value can break serialization. This is true whether you're using `Int` raw values or `String` raw values; it's just that `Int`s are a little easier to change.

If you create an enum with raw values, the raw values are more or less part of that enum's contract. You shouldn't expect things to continue to work properly if you change them, any more than you should expect them to continue to work properly if you delete a case. In fact, the library resilience document specifically calls out changing raw values as a "binary-compatible source-breaking change": <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst#enums>

> I agree there are lots of important and more difficult things to review in the language but I wouldn't be wasting my time here if I didn't think this was equally important.

I don't see how it's particularly important to solve this bizarre corner case of needing both a non-String raw value *and* a way to look up cases by name, particularly since you *still* have not provided a decent example where the non-String rawValue ought to be a rawValue at all.

Brent Royal-Gordon

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160602/9ef30c54/attachment.html>

More information about the swift-evolution mailing list