Could enums have their rawValue type inferred?


(Eric Miller) #1

This might open a larger can of worms than I imagine, but what do you folks
think about using the `rawValue` of an enum when that rawValue is a fit for
the expected type?

Use case.

I'm making an animation facade, and it has some speed presets:

class Animate {
  enum transitionSpeed: NSTimeInterval {
    case fast = 0.15
    case slow = 0.5
  }
  enum ambientAnimationSpeed: NSTimeInterval {
    case fast = 1.0
    case slow = 5.0
  }
  ...
}

I did them with static variables at first but that made the call site
verbose. Compare:

Animate.fadeIn(view, withSpeed: Animate.cfg.transitionFast)
Animate.fadeIn(view, withSpeed: .fast)

So, I like the enum approach better, but my library code has to use
`rawValue` to do anything with the actual value, of course:

static func fadeIn(view: UIView?, withSpeed duration:transitionSpeed =
.fast) {
  ...
  UIView.animateWithDuration(duration.rawValue, animations: { })
}

It's not a serious issue, but the code is more clear and beautiful if it
has just myIntent, rather than myIntent.rawValue.

I've hit this issue when modeling other things, such as:

* server fault codes
* HTTP status codes
* Currency codes
* Days of the week

Would it be appropriate to "autocast" to the rawValue of the enum when the
rawValue's Type matches the type expectation of the API? Or would this
introduce a bunch of type system uncertainty?

Maybe this could be implemented as a protocol? It feels almost like the
convenience of `CustomStringConvertible`'s `description` property.


(Leonardo Pessoa) #2

Eric, I think I understood your proposal. If I may explain in other words
it would be "to automatically cast rawValue when assigning an enum value to
a variable or argument of the type of the enum's raw value", am I right? I
think this would imply a little more inference and type checking rules from
the compiler and maybe even take a little longer to fully compile code. I'm
not sure it's feasible but from your examples, I can see how it enhances
readability of the code, so I'm +1 for it. My only concern is that you
would still need to fully declare the enum's name where the value of the
enum is used. Taking from your own example

    Animate.fadeIn(view, withSpeed: .fast)

couldn't be called that way if withSpeed expects and NSTimeInterval because
the compiler won't know whether you're refering to transitionSpeed or to
ambientAnimationSpeed. You would still have to call it like

    Animate.fadeIn(view, withSpeed: transitionSpeed.fast)

even if you had only one possible enum value over all declared enums
because that would still force the compiler to search for each value over
all known enums to define where the value you're using comes from and make
sure there are no two enums with the same value.

Aside from that, I good with the idea.

···

On 13 May 2016 at 15:09, Eric Miller via swift-evolution < swift-evolution@swift.org> wrote:

This might open a larger can of worms than I imagine, but what do you
folks think about using the `rawValue` of an enum when that rawValue is a
fit for the expected type?

Use case.

I'm making an animation facade, and it has some speed presets:

class Animate {
  enum transitionSpeed: NSTimeInterval {
    case fast = 0.15
    case slow = 0.5
  }
  enum ambientAnimationSpeed: NSTimeInterval {
    case fast = 1.0
    case slow = 5.0
  }
  ...
}

I did them with static variables at first but that made the call site
verbose. Compare:

Animate.fadeIn(view, withSpeed: Animate.cfg.transitionFast)
Animate.fadeIn(view, withSpeed: .fast)

So, I like the enum approach better, but my library code has to use
`rawValue` to do anything with the actual value, of course:

static func fadeIn(view: UIView?, withSpeed duration:transitionSpeed =
.fast) {
  ...
  UIView.animateWithDuration(duration.rawValue, animations: { })
}

It's not a serious issue, but the code is more clear and beautiful if it
has just myIntent, rather than myIntent.rawValue.

I've hit this issue when modeling other things, such as:

* server fault codes
* HTTP status codes
* Currency codes
* Days of the week

Would it be appropriate to "autocast" to the rawValue of the enum when the
rawValue's Type matches the type expectation of the API? Or would this
introduce a bunch of type system uncertainty?

Maybe this could be implemented as a protocol? It feels almost like the
convenience of `CustomStringConvertible`'s `description` property.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #3

This might open a larger can of worms than I imagine, but what do you folks think about using the `rawValue` of an enum when that rawValue is a fit for the expected type?

No.

An enum case is something quite distinct from its raw value. If you just want a convenient way to write some particular value you use a lot, what you're looking for is a constant, not an enum.

Use case.

I'm making an animation facade, and it has some speed presets:

class Animate {
  enum transitionSpeed: NSTimeInterval {
    case fast = 0.15
    case slow = 0.5
  }
  enum ambientAnimationSpeed: NSTimeInterval {
    case fast = 1.0
    case slow = 5.0
  }
  ...
}

I did them with static variables at first but that made the call site verbose. Compare:

Animate.fadeIn(view, withSpeed: Animate.cfg.transitionFast)
Animate.fadeIn(view, withSpeed: .fast)

So, I like the enum approach better, but my library code has to use `rawValue` to do anything with the actual value, of course:

static func fadeIn(view: UIView?, withSpeed duration:transitionSpeed = .fast) {
  ...
  UIView.animateWithDuration(duration.rawValue, animations: { })
}

For instance, in this case, you should just write something like:

  extension NSTimeInterval {
    static let fastTransition: NSTimeInterval = 0.15
    static let slowTransition: NSTimeInterval = 0.5
    
    static let fastAmbientAnimation: NSTimeInterval = 1.0
    static let slowAmbientAnimation: NSTimeInterval = 5.0
  }
  
  class Animate {
    static func fadeIn(view: UIView?, withSpeed duration: NSTimeInterval = .fastTransition) {
      ...
      UIView.animateWithDuration(duration, animations: { })
    }
  }
  
  Animate.fadeIn(view, withSpeed: .fastTransition)
  Animate.fadeIn(view, withSpeed: .slowTransition)
  Animate.fadeIn(view, withSpeed: 3.14159) // or anything else

Alternatively, if you really do want to lock people into either "fast" or "slow", then by all means use an enum. But realize that your new type is just that: a new type. It has its own semantic meaning, its own operations and methods and properties, and its own set of permitted values. It is not interchangeable with the raw value.

···

--
Brent Royal-Gordon
Architechies


(Tino) #4

I'm not sure if I want the proposal to be standard-behavior, but in the past, I have written numerous extensions which basically took an enum to forward its raw value to an existing method (mostly keys for NSUserDefaults).
So, it would be convenient if the conversation happens automatically… but this use case isn't actually a "real" enum; it is just handy to get the value and a namespace for free, and I expect that there will be better ways to achieve this in the future.
On the other hand: What's the point of "raw-value enums"? Are they just a bridge-technogy, or is it ok to use them to store constants?


(Brent Royal-Gordon) #5

On the other hand: What's the point of "raw-value enums"? Are they just a bridge-technogy, or is it ok to use them to store constants?

I think of RawRepresentable (the protocol behind rawValue) as a serialization mechanism, basically the Swift standard library's answer to NSCoding. Thus, rawValue is a way to serialize your enum into an Int or String you know how to read and write from disk. More broadly, I often apply RawRepresentable to structs and have them return plist- or JSON-compatible dictionaries.

···

--
Brent Royal-Gordon
Architechies


(Austin Zheng) #6

Oh, this is a really interesting use case for that protocol. I wonder if
RawRepresentable might one day be auto-derivable for certain types, as part
of a more comprehensive Swift serialization/deserialization feature.

Austin

···

On Tue, May 17, 2016 at 1:05 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> On the other hand: What's the point of "raw-value enums"? Are they just
a bridge-technogy, or is it ok to use them to store constants?

I think of RawRepresentable (the protocol behind rawValue) as a
serialization mechanism, basically the Swift standard library's answer to
NSCoding. Thus, rawValue is a way to serialize your enum into an Int or
String you know how to read and write from disk. More broadly, I often
apply RawRepresentable to structs and have them return plist- or
JSON-compatible dictionaries.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Goffredo Marocchi) #7

Thanks Brent, this one is quite a keeper :). Concise and very friendly way of explaining RawRepresentable.

···

Sent from my iPhone

On 17 May 2016, at 21:05, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On the other hand: What's the point of "raw-value enums"? Are they just a bridge-technogy, or is it ok to use them to store constants?

I think of RawRepresentable (the protocol behind rawValue) as a serialization mechanism, basically the Swift standard library's answer to NSCoding. Thus, rawValue is a way to serialize your enum into an Int or String you know how to read and write from disk. More broadly, I often apply RawRepresentable to structs and have them return plist- or JSON-compatible dictionaries.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution