> 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 }
...etc...
}
(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
Architechies
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution