[Proposal] Enums with static stored properties for each case


(Jānis Kiršteins) #1

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
    case mercury
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune

    var mass: Float {
        switch self {
        case .mercury: return 3.303e+23
        case .venus: return 4.869e+24
        case .earth: return 5.976e+24
        case .mars: return 6.421e+23
        case .jupiter: return 1.9e+27
        case .saturn: return 5.688e+26
        case .uranus: return 8.686e+25
        case .neptune: return 1.024e+26
        }
    }

    var radius: Float {
        switch self {
        case .mercury: return 2.4397e6
        case .venus: return 6.0518e6
        case .earth: return 6.37814e6
        case .mars: return 3.3972e6
        case .jupiter: return 7.1492e7
        case .saturn: return 6.0268e7
        case .uranus: return 2.5559e7
        case .neptune: return 2.4746e7
        }
    }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
    var mass: Float
    var radius: Float

    static init(mass: Float, radius: Float) {
        self.mass = mass
        self.radius = radius
    }

    case mercury where (mass: 3.303e+23, radius: 2.4397e6)
    case venus where (mass: 4.869e+24, radius: 6.0518e6)
    case earth where (mass: 5.976e+24, radius: 6.37814e6)
    case mars where (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
    case saturn where (mass: 5.688e+26, radius: 6.0268e7)
    case uranus where (mass: 8.686e+25, radius: 2.5559e7)
    case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed


(Leonardo Pessoa) #2

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

···

-----Original Message-----
From: "Jānis Kiršteins via swift-evolution" <swift-evolution@swift.org>
Sent: ‎25/‎05/‎2016 08:58 AM
To: "swift-evolution@swift.org" <swift-evolution@swift.org>
Subject: [swift-evolution] [Proposal] Enums with static stored properties foreach case

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
    case mercury
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune

    var mass: Float {
        switch self {
        case .mercury: return 3.303e+23
        case .venus: return 4.869e+24
        case .earth: return 5.976e+24
        case .mars: return 6.421e+23
        case .jupiter: return 1.9e+27
        case .saturn: return 5.688e+26
        case .uranus: return 8.686e+25
        case .neptune: return 1.024e+26
        }
    }

    var radius: Float {
        switch self {
        case .mercury: return 2.4397e6
        case .venus: return 6.0518e6
        case .earth: return 6.37814e6
        case .mars: return 3.3972e6
        case .jupiter: return 7.1492e7
        case .saturn: return 6.0268e7
        case .uranus: return 2.5559e7
        case .neptune: return 2.4746e7
        }
    }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
    var mass: Float
    var radius: Float

    static init(mass: Float, radius: Float) {
        self.mass = mass
        self.radius = radius
    }

    case mercury where (mass: 3.303e+23, radius: 2.4397e6)
    case venus where (mass: 4.869e+24, radius: 6.0518e6)
    case earth where (mass: 5.976e+24, radius: 6.37814e6)
    case mars where (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
    case saturn where (mass: 5.688e+26, radius: 6.0268e7)
    case uranus where (mass: 8.686e+25, radius: 2.5559e7)
    case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Sweeris) #3

This would be my preferred solution… AFAIK, the only reason we can’t do it now is that Swift currently requires RawValue be an integer, floating-point value, or string. I don’t know why the language has this restriction, so I can’t comment on how hard it would be to change.

- Dave Sweeris

···

On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}


(Jānis Kiršteins) #4

That would replace current enum raw value functionality and I see two
problems with that.

1. A lot of breaking changes
2. Raw values currently are unique values among all cases. That makes
a possibility that enums can be easily serialized/deserialized to
formats like JSON, property lists, etc. In "case mercury = (mass:
3.303e+23, radius: 2.4397e6)" neither mass nor radius is unique value
(it is possible that two different planets could have the same mass as
radius).

···

On Wed, May 25, 2016 at 3:37 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}
________________________________
From: Jānis Kiršteins via swift-evolution
Sent: ‎25/‎05/‎2016 08:58 AM
To: swift-evolution@swift.org
Subject: [swift-evolution] [Proposal] Enums with static stored properties
foreach case

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
    case mercury
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune

    var mass: Float {
        switch self {
        case .mercury: return 3.303e+23
        case .venus: return 4.869e+24
        case .earth: return 5.976e+24
        case .mars: return 6.421e+23
        case .jupiter: return 1.9e+27
        case .saturn: return 5.688e+26
        case .uranus: return 8.686e+25
        case .neptune: return 1.024e+26
        }
    }

    var radius: Float {
        switch self {
        case .mercury: return 2.4397e6
        case .venus: return 6.0518e6
        case .earth: return 6.37814e6
        case .mars: return 3.3972e6
        case .jupiter: return 7.1492e7
        case .saturn: return 6.0268e7
        case .uranus: return 2.5559e7
        case .neptune: return 2.4746e7
        }
    }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
    var mass: Float
    var radius: Float

    static init(mass: Float, radius: Float) {
        self.mass = mass
        self.radius = radius
    }

    case mercury where (mass: 3.303e+23, radius: 2.4397e6)
    case venus where (mass: 4.869e+24, radius: 6.0518e6)
    case earth where (mass: 5.976e+24, radius: 6.37814e6)
    case mars where (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
    case saturn where (mass: 5.688e+26, radius: 6.0268e7)
    case uranus where (mass: 8.686e+25, radius: 2.5559e7)
    case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #5

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

My opinions so far:

- Abusing rawValue is just that: an abuse.

- Using `where` just doesn't match the use of `where` elsewhere in the language; everywhere else, it's some kind of condition.

- Dictionaries are the most straightforward way to handle this with the current language, but their lack of exhaustiveness checking is a problem.

What I would do is borrow the "accessors" concept from the property behaviors proposal and extend it so that it supported both functions and variables. Then I would let you write this:

  enum Planet {
     accessor var mass: Float
     accessor var radius: Float
     
     case mercury {
        mass = 3.303e+23
        radius = 2.4397e6
     }
     case venus {
        mass = 4.869e+24
        radius = 6.0518e6
     }
     case earth {
        mass = 5.976e+24
        radius = 6.37814e6
     }
     case mars {
        mass = 6.421e+23
        radius = 3.3972e6
     }
     case jupiter {
        mass = 1.9e+27
        radius = 7.1492e7
     }
     case saturn {
        mass = 5.688e+26
        radius = 6.0268e7
     }
     case uranus {
        mass = 8.686e+25
        radius = 2.5559e7
     }
     case neptune {
        mass = 1.024e+26
        radius = 2.4746e7
     }
  }

You would also be able to declare methods like this; each implementation would just look like `methodName { code }`. And you could provide default implementations too:

  enum Planet {
     accessor var mass: Float
     accessor var radius: Float
     accessor var habitable: Bool = false
     
     case mercury {
        mass = 3.303e+23
        radius = 2.4397e6
     }
     case venus {
        mass = 4.869e+24
        radius = 6.0518e6
     }
     case earth {
        mass = 5.976e+24
        radius = 6.37814e6
        habitable = true
     }
     case mars {
        mass = 6.421e+23
        radius = 3.3972e6
     }
     case jupiter {
        mass = 1.9e+27
        radius = 7.1492e7
     }
     case saturn {
        mass = 5.688e+26
        radius = 6.0268e7
     }
     case uranus {
        mass = 8.686e+25
        radius = 2.5559e7
     }
     case neptune {
        mass = 1.024e+26
        radius = 2.4746e7
     }
  }

···

--
Brent Royal-Gordon
Architechies


(Leonardo Pessoa) #6

But that's an issue related to how serialising JSON works. Should the API serialise the name of the value and not the associated values, it should work fine. Should we have anything like annotations in Swift to describe how we wanted that output we'd have no problems.

About breaking things I wouldn't worry as Swift 3 is going to break a lot by itself anyway.

···

-----Original Message-----
From: "Jānis Kiršteins" <janis.kirsteins@gmail.com>
Sent: ‎25/‎05/‎2016 04:50 PM
To: "Leonardo Pessoa" <me@lmpessoa.com>
Cc: "swift-evolution@swift.org" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesforeach case

That would replace current enum raw value functionality and I see two
problems with that.

1. A lot of breaking changes
2. Raw values currently are unique values among all cases. That makes
a possibility that enums can be easily serialized/deserialized to
formats like JSON, property lists, etc. In "case mercury = (mass:
3.303e+23, radius: 2.4397e6)" neither mass nor radius is unique value
(it is possible that two different planets could have the same mass as
radius).

On Wed, May 25, 2016 at 3:37 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}
________________________________
From: Jānis Kiršteins via swift-evolution
Sent: ‎25/‎05/‎2016 08:58 AM
To: swift-evolution@swift.org
Subject: [swift-evolution] [Proposal] Enums with static stored properties
foreach case

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
    case mercury
    case venus
    case earth
    case mars
    case jupiter
    case saturn
    case uranus
    case neptune

    var mass: Float {
        switch self {
        case .mercury: return 3.303e+23
        case .venus: return 4.869e+24
        case .earth: return 5.976e+24
        case .mars: return 6.421e+23
        case .jupiter: return 1.9e+27
        case .saturn: return 5.688e+26
        case .uranus: return 8.686e+25
        case .neptune: return 1.024e+26
        }
    }

    var radius: Float {
        switch self {
        case .mercury: return 2.4397e6
        case .venus: return 6.0518e6
        case .earth: return 6.37814e6
        case .mars: return 3.3972e6
        case .jupiter: return 7.1492e7
        case .saturn: return 6.0268e7
        case .uranus: return 2.5559e7
        case .neptune: return 2.4746e7
        }
    }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
    var mass: Float
    var radius: Float

    static init(mass: Float, radius: Float) {
        self.mass = mass
        self.radius = radius
    }

    case mercury where (mass: 3.303e+23, radius: 2.4397e6)
    case venus where (mass: 4.869e+24, radius: 6.0518e6)
    case earth where (mass: 5.976e+24, radius: 6.37814e6)
    case mars where (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
    case saturn where (mass: 5.688e+26, radius: 6.0268e7)
    case uranus where (mass: 8.686e+25, radius: 2.5559e7)
    case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jacob Bandes-Storch) #7

Except you'd have to write Planet.mercury.rawValue.mass, rather than
Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and
allow `TupleName.caseName.propertyName` to access a tuple element without
going through .rawValue.

···

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution < > swift-evolution@swift.org> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it
now is that Swift currently requires RawValue be an integer, floating-point
value, or string. I don’t know why the language has this restriction, so I
can’t comment on how hard it would be to change.

- Dave Sweeris


(David Sweeris) #8

Good point… Has there been a thread on allowing raw-valued enums to be treated as constants of type `RawValue` yet? Either way, removing the restriction on what types can be a RawValue is still my preferred solution.

- Dave Sweeris

···

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it now is that Swift currently requires RawValue be an integer, floating-point value, or string. I don’t know why the language has this restriction, so I can’t comment on how hard it would be to change.

- Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and allow `TupleName.caseName.propertyName` to access a tuple element without going through .rawValue.


(Patrick Smith) #9

Yes, I don’t think it would work with a raw value behaviour. You want it to compile down to the same underlying code as the first example, without having to write lots of switch statements.

Another syntax I could imagine is:

enum Planet {
  var mass: Float { get }
  var radius: Float { get }

  case mercury [
    mass: 3.303e+23,
    radius: 2.4397e6
  ]
  case venus [
    mass: 4.869e+24,
    radius: 6.0518e6
  ]
  case earth [
    mass: 5.976e+24,
    radius: 6.37814e6
  ]
  ...
}

You couldn’t have an initializer, as enums only have storage when they have associated values, which these do not. ‘where’ is used for pattern matching, not declaring as far as I know, so that’s why I suggest this other way.

Patrick

···

On 26 May 2016, at 5:50 AM, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

That would replace current enum raw value functionality and I see two
problems with that.

1. A lot of breaking changes
2. Raw values currently are unique values among all cases. That makes
a possibility that enums can be easily serialized/deserialized to
formats like JSON, property lists, etc. In "case mercury = (mass:
3.303e+23, radius: 2.4397e6)" neither mass nor radius is unique value
(it is possible that two different planets could have the same mass as
radius).

On Wed, May 25, 2016 at 3:37 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
   case mercury = (mass: 3.303e+23, radius: 2.4397e6)
   case venus = (mass: 4.869e+24, radius: 6.0518e6)
   case earth = (mass: 5.976e+24, radius: 6.37814e6)
   case mars = (mass: 6.421e+23, radius: 3.3972e6)
   case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
   case saturn = (mass: 5.688e+26, radius: 6.0268e7)
   case uranus = (mass: 8.686e+25, radius: 2.5559e7)
   case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}
________________________________
From: Jānis Kiršteins via swift-evolution
Sent: ‎25/‎05/‎2016 08:58 AM
To: swift-evolution@swift.org
Subject: [swift-evolution] [Proposal] Enums with static stored properties
foreach case

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
   case mercury
   case venus
   case earth
   case mars
   case jupiter
   case saturn
   case uranus
   case neptune

   var mass: Float {
       switch self {
       case .mercury: return 3.303e+23
       case .venus: return 4.869e+24
       case .earth: return 5.976e+24
       case .mars: return 6.421e+23
       case .jupiter: return 1.9e+27
       case .saturn: return 5.688e+26
       case .uranus: return 8.686e+25
       case .neptune: return 1.024e+26
       }
   }

   var radius: Float {
       switch self {
       case .mercury: return 2.4397e6
       case .venus: return 6.0518e6
       case .earth: return 6.37814e6
       case .mars: return 3.3972e6
       case .jupiter: return 7.1492e7
       case .saturn: return 6.0268e7
       case .uranus: return 2.5559e7
       case .neptune: return 2.4746e7
       }
   }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
   var mass: Float
   var radius: Float

   static init(mass: Float, radius: Float) {
       self.mass = mass
       self.radius = radius
   }

   case mercury where (mass: 3.303e+23, radius: 2.4397e6)
   case venus where (mass: 4.869e+24, radius: 6.0518e6)
   case earth where (mass: 5.976e+24, radius: 6.37814e6)
   case mars where (mass: 6.421e+23, radius: 3.3972e6)
   case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
   case saturn where (mass: 5.688e+26, radius: 6.0268e7)
   case uranus where (mass: 8.686e+25, radius: 2.5559e7)
   case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Matthew Johnson) #10

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

My opinions so far:

- Abusing rawValue is just that: an abuse.

- Using `where` just doesn't match the use of `where` elsewhere in the language; everywhere else, it's some kind of condition.

- Dictionaries are the most straightforward way to handle this with the current language, but their lack of exhaustiveness checking is a problem.

What I would do is borrow the "accessors" concept from the property behaviors proposal and extend it so that it supported both functions and variables. Then I would let you write this:

  enum Planet {
     accessor var mass: Float
     accessor var radius: Float
     
     case mercury {
        mass = 3.303e+23
        radius = 2.4397e6
     }
     case venus {
        mass = 4.869e+24
        radius = 6.0518e6
     }
     case earth {
        mass = 5.976e+24
        radius = 6.37814e6
     }
     case mars {
        mass = 6.421e+23
        radius = 3.3972e6
     }
     case jupiter {
        mass = 1.9e+27
        radius = 7.1492e7
     }
     case saturn {
        mass = 5.688e+26
        radius = 6.0268e7
     }
     case uranus {
        mass = 8.686e+25
        radius = 2.5559e7
     }
     case neptune {
        mass = 1.024e+26
        radius = 2.4746e7
     }
  }

You would also be able to declare methods like this; each implementation would just look like `methodName { code }`. And you could provide default implementations too:

  enum Planet {
     accessor var mass: Float
     accessor var radius: Float
     accessor var habitable: Bool = false
     
     case mercury {
        mass = 3.303e+23
        radius = 2.4397e6
     }
     case venus {
        mass = 4.869e+24
        radius = 6.0518e6
     }
     case earth {
        mass = 5.976e+24
        radius = 6.37814e6
        habitable = true
     }
     case mars {
        mass = 6.421e+23
        radius = 3.3972e6
     }
     case jupiter {
        mass = 1.9e+27
        radius = 7.1492e7
     }
     case saturn {
        mass = 5.688e+26
        radius = 6.0268e7
     }
     case uranus {
        mass = 8.686e+25
        radius = 2.5559e7
     }
     case neptune {
        mass = 1.024e+26
        radius = 2.4746e7
     }
  }

This is the first really interesting (to me) idea in the thread. I think I like it but need to give it more thought to decide for sure.

One really interesting thing we could do to build on this would be to introduce the associated value names inside the scope of each case as if they were member variables:

enum Foo {
    accessor func bar() -> Int
    
    case baz(val: Int) {
        // val is in scope here
        func bar() {
            return val
        }
    }
}

···

On May 26, 2016, at 4:47 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

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


(Charles Srstka) #11

In addition, enums with associated types can’t have rawValues.

Why is this relevant, you may ask? Because error enums are a huge use case for something like this. Being able to do the below would be great:

enum MyError: ErrorProtocol {
    accessor var localizedFailureReason: String
    accessor var url: NSURL

    case FileNotFound(url: NSURL) {
        self.localizedFailureReason = “File \"\(url.lastPathComponent ?? “”)\” not found.”
        self.url = url
    }

    case FileIsCorrupt(url: NSURL) {
        self.localizedFailureReason = “File \"\(url.lastPathComponent ?? “”)\” is corrupt.”
        self.url = url
    }
}

This would be much cleaner than the existing method of using a switch to create a userInfo dictionary for creating an NSError to send to -[NSApplication presentError:] and similar methods.

Charles

···

On May 26, 2016, at 4:47 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

- Abusing rawValue is just that: an abuse.


(Leonardo Pessoa) #12

I'd still go with tuple syntax as it feels more like a natural extension to current enum syntax and does not introduce new elements to the syntax of enums (other than add tuples as a possible enum value type) thus being simpler and faster to implement and learn than a new syntax built specifically for this kind of construction.

As I mentioned before, the issue with JSON and other engines trying to record the raw value instead of the enum seems to me as a wrong implementation choice of the engine. Previous to Swift enums I've always seen enum cases the same as constants and any additional values they'd hold are associated with that constant and not persisted. This may also be a thing from the company I work for today that choses to store the names of the enum cases (as strings) in databases and any values associated with them are recovered from the enum case constant. Of course the language I work with supports finding the enum value by its name, which it seems Swift doesn't.

···

-----Original Message-----
From: "Patrick Smith" <pgwsmith@gmail.com>
Sent: ‎25/‎05/‎2016 10:20 PM
To: "Jānis Kiršteins" <janis.kirsteins@gmail.com>
Cc: "Leonardo Pessoa" <me@lmpessoa.com>; "swift-evolution@swift.org" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesforeach case

Yes, I don’t think it would work with a raw value behaviour. You want it to compile down to the same underlying code as the first example, without having to write lots of switch statements.

Another syntax I could imagine is:

enum Planet {
  var mass: Float { get }
  var radius: Float { get }

  case mercury [
    mass: 3.303e+23,
    radius: 2.4397e6
  ]
  case venus [
    mass: 4.869e+24,
    radius: 6.0518e6
  ]
  case earth [
    mass: 5.976e+24,
    radius: 6.37814e6
  ]
  ...
}

You couldn’t have an initializer, as enums only have storage when they have associated values, which these do not. ‘where’ is used for pattern matching, not declaring as far as I know, so that’s why I suggest this other way.

Patrick

On 26 May 2016, at 5:50 AM, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

That would replace current enum raw value functionality and I see two
problems with that.

1. A lot of breaking changes
2. Raw values currently are unique values among all cases. That makes
a possibility that enums can be easily serialized/deserialized to
formats like JSON, property lists, etc. In "case mercury = (mass:
3.303e+23, radius: 2.4397e6)" neither mass nor radius is unique value
(it is possible that two different planets could have the same mass as
radius).

On Wed, May 25, 2016 at 3:37 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
   case mercury = (mass: 3.303e+23, radius: 2.4397e6)
   case venus = (mass: 4.869e+24, radius: 6.0518e6)
   case earth = (mass: 5.976e+24, radius: 6.37814e6)
   case mars = (mass: 6.421e+23, radius: 3.3972e6)
   case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
   case saturn = (mass: 5.688e+26, radius: 6.0268e7)
   case uranus = (mass: 8.686e+25, radius: 2.5559e7)
   case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}
________________________________
From: Jānis Kiršteins via swift-evolution
Sent: ‎25/‎05/‎2016 08:58 AM
To: swift-evolution@swift.org
Subject: [swift-evolution] [Proposal] Enums with static stored properties
foreach case

Hello everyone,

Currently Swift only supports computed properties for each enum case.
If you want to somehow get static values with each case you would
probably do it like this:

enum Planet {
   case mercury
   case venus
   case earth
   case mars
   case jupiter
   case saturn
   case uranus
   case neptune

   var mass: Float {
       switch self {
       case .mercury: return 3.303e+23
       case .venus: return 4.869e+24
       case .earth: return 5.976e+24
       case .mars: return 6.421e+23
       case .jupiter: return 1.9e+27
       case .saturn: return 5.688e+26
       case .uranus: return 8.686e+25
       case .neptune: return 1.024e+26
       }
   }

   var radius: Float {
       switch self {
       case .mercury: return 2.4397e6
       case .venus: return 6.0518e6
       case .earth: return 6.37814e6
       case .mars: return 3.3972e6
       case .jupiter: return 7.1492e7
       case .saturn: return 6.0268e7
       case .uranus: return 2.5559e7
       case .neptune: return 2.4746e7
       }
   }
}

However I see two problems with this approach:

1. These value definitions are spread out and difficult to read and
maintain (especially if you have many computed properties for each
enum case);
2. These values are not static. They are computed each time property
is accessed. This can be a problem when value is expensive to create.

The proposed solution is to have single static initializer for each
enum case that initializes stored properties. For example,

enum Planet {
   var mass: Float
   var radius: Float

   static init(mass: Float, radius: Float) {
       self.mass = mass
       self.radius = radius
   }

   case mercury where (mass: 3.303e+23, radius: 2.4397e6)
   case venus where (mass: 4.869e+24, radius: 6.0518e6)
   case earth where (mass: 5.976e+24, radius: 6.37814e6)
   case mars where (mass: 6.421e+23, radius: 3.3972e6)
   case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
   case saturn where (mass: 5.688e+26, radius: 6.0268e7)
   case uranus where (mass: 8.686e+25, radius: 2.5559e7)
   case neptune where (mass: 1.024e+26, radius: 2.4746e7)
}

This approach do not affect enums that have raw or associated values,
or custom enum initializers:

case A = "A" where (id: 0)

or

case B(Int, Int, Int) where (id: 0)

Benefits:
1. Less verbosity
2. Improved readability
3. Related values are closer to each other
4. Static values are not recomputed
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Patrick Smith) #13

Yes, that would be a great behaviour, but that’s exactly how enums with raw values do not work. The enum cases transform to and from an external representable form, that’s why it is ‘RawRepresentable’. So in your example of tuples, that representable form would be the tuple values themselves.

I agree about having enum cases as strings is handy. For your described case it is best to use a String as the raw value type, and the derived values as calculated properties. It would be handy, as we are discussing, to declare those calculated properties in an easier manner, and I would’t mind if it used a tuple syntax. But it should’t be used as the underlying value, as this is the source of ‘truth’ for the enum.

···

On 26 May 2016, at 12:09 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I'd still go with tuple syntax as it feels more like a natural extension to current enum syntax and does not introduce new elements to the syntax of enums (other than add tuples as a possible enum value type) thus being simpler and faster to implement and learn than a new syntax built specifically for this kind of construction.

As I mentioned before, the issue with JSON and other engines trying to record the raw value instead of the enum seems to me as a wrong implementation choice of the engine. Previous to Swift enums I've always seen enum cases the same as constants and any additional values they'd hold are associated with that constant and not persisted. This may also be a thing from the company I work for today that choses to store the names of the enum cases (as strings) in databases and any values associated with them are recovered from the enum case constant. Of course the language I work with supports finding the enum value by its name, which it seems Swift doesn't.

From: Patrick Smith <mailto:pgwsmith@gmail.com>
Sent: ‎25/‎05/‎2016 10:20 PM
To: Jānis Kiršteins <mailto:janis.kirsteins@gmail.com>
Cc: Leonardo Pessoa <mailto:me@lmpessoa.com>; swift-evolution@swift.org <mailto:swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesforeach case

Yes, I don’t think it would work with a raw value behaviour. You want it to compile down to the same underlying code as the first example, without having to write lots of switch statements.

Another syntax I could imagine is:

enum Planet {
  var mass: Float { get }
  var radius: Float { get }

  case mercury [
    mass: 3.303e+23,
    radius: 2.4397e6
  ]
  case venus [
    mass: 4.869e+24,
    radius: 6.0518e6
  ]
  case earth [
    mass: 5.976e+24,
    radius: 6.37814e6
  ]
  ...
}

You couldn’t have an initializer, as enums only have storage when they have associated values, which these do not. ‘where’ is used for pattern matching, not declaring as far as I know, so that’s why I suggest this other way.

Patrick

> On 26 May 2016, at 5:50 AM, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:
>
> That would replace current enum raw value functionality and I see two
> problems with that.
>
> 1. A lot of breaking changes
> 2. Raw values currently are unique values among all cases. That makes
> a possibility that enums can be easily serialized/deserialized to
> formats like JSON, property lists, etc. In "case mercury = (mass:
> 3.303e+23, radius: 2.4397e6)" neither mass nor radius is unique value
> (it is possible that two different planets could have the same mass as
> radius).
>
>
>
> On Wed, May 25, 2016 at 3:37 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:
>> Hi,
>>
>> Couldn't this be solved by using tuples? If not because the syntax is not
>> allowed I think this would be more coherent to do it using current syntax.
>>
>> enum Planet : (mass: Float, radius: Float) {
>> case mercury = (mass: 3.303e+23, radius: 2.4397e6)
>> case venus = (mass: 4.869e+24, radius: 6.0518e6)
>> case earth = (mass: 5.976e+24, radius: 6.37814e6)
>> case mars = (mass: 6.421e+23, radius: 3.3972e6)
>> case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
>> case saturn = (mass: 5.688e+26, radius: 6.0268e7)
>> case uranus = (mass: 8.686e+25, radius: 2.5559e7)
>> case neptune = (mass: 1.024e+26, radius: 2.4746e7)
>> }
>> ________________________________
>> From: Jānis Kiršteins via swift-evolution
>> Sent: ‎25/‎05/‎2016 08:58 AM
>> To: swift-evolution@swift.org
>> Subject: [swift-evolution] [Proposal] Enums with static stored properties
>> foreach case
>>
>> Hello everyone,
>>
>> Currently Swift only supports computed properties for each enum case.
>> If you want to somehow get static values with each case you would
>> probably do it like this:
>>
>> enum Planet {
>> case mercury
>> case venus
>> case earth
>> case mars
>> case jupiter
>> case saturn
>> case uranus
>> case neptune
>>
>> var mass: Float {
>> switch self {
>> case .mercury: return 3.303e+23
>> case .venus: return 4.869e+24
>> case .earth: return 5.976e+24
>> case .mars: return 6.421e+23
>> case .jupiter: return 1.9e+27
>> case .saturn: return 5.688e+26
>> case .uranus: return 8.686e+25
>> case .neptune: return 1.024e+26
>> }
>> }
>>
>> var radius: Float {
>> switch self {
>> case .mercury: return 2.4397e6
>> case .venus: return 6.0518e6
>> case .earth: return 6.37814e6
>> case .mars: return 3.3972e6
>> case .jupiter: return 7.1492e7
>> case .saturn: return 6.0268e7
>> case .uranus: return 2.5559e7
>> case .neptune: return 2.4746e7
>> }
>> }
>> }
>>
>> However I see two problems with this approach:
>>
>> 1. These value definitions are spread out and difficult to read and
>> maintain (especially if you have many computed properties for each
>> enum case);
>> 2. These values are not static. They are computed each time property
>> is accessed. This can be a problem when value is expensive to create.
>>
>> The proposed solution is to have single static initializer for each
>> enum case that initializes stored properties. For example,
>>
>> enum Planet {
>> var mass: Float
>> var radius: Float
>>
>> static init(mass: Float, radius: Float) {
>> self.mass = mass
>> self.radius = radius
>> }
>>
>> case mercury where (mass: 3.303e+23, radius: 2.4397e6)
>> case venus where (mass: 4.869e+24, radius: 6.0518e6)
>> case earth where (mass: 5.976e+24, radius: 6.37814e6)
>> case mars where (mass: 6.421e+23, radius: 3.3972e6)
>> case jupiter where (mass: 1.9e+27, radius: 7.1492e7)
>> case saturn where (mass: 5.688e+26, radius: 6.0268e7)
>> case uranus where (mass: 8.686e+25, radius: 2.5559e7)
>> case neptune where (mass: 1.024e+26, radius: 2.4746e7)
>> }
>>
>> This approach do not affect enums that have raw or associated values,
>> or custom enum initializers:
>>
>> case A = "A" where (id: 0)
>>
>> or
>>
>> case B(Int, Int, Int) where (id: 0)
>>
>> Benefits:
>> 1. Less verbosity
>> 2. Improved readability
>> 3. Related values are closer to each other
>> 4. Static values are not recomputed
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


(Patrick Smith) #14

Raw values are for coverting to and from an external representation only. That’s why it must be a primitive value, as they can be checked for equality in `init?(rawValue:)`.

The planets here have fuzzy floating point values, and so must never be checked for equality. The only source of truth are their names.

If you want a list of planets, the best way is to do this:

struct Planet {
  var mass: Float
  var radius: Float
  
  static let mercury = Planet(mass: 3.303e+23, radius: 2.4397e6)
  static let venus = Planet(mass: 4.869e+24, radius: 6.0518e6)
  static let earth = Planet(mass: 5.976e+24, radius: 6.37814e6)
  static let mars = Planet(mass: 6.421e+23, radius: 3.3972e6)
  static let jupiter = Planet(mass: 1.9e+27, radius: 7.1492e7)
  static let saturn = Planet(mass: 5.688e+26, radius: 6.0268e7)
  static let uranus = Planet(mass: 8.686e+25, radius: 2.5559e7)
  static let neptune = Planet(mass: 1.024e+26, radius: 2.4746e7)
}

What this proposal is asking for is an easier way to have derived values from enum cases. Asking for more flexible RawValues means mass and radius are not derived, they are the source of truth. It goes against the whole point of RawRepresentable. You are not saying ‘Mercury is identified by the case .mercury’, you are saying ‘Mercury is identified by a mass of 3.303e+23’. It’s backwards.

···

On 26 May 2016, at 1:47 PM, David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it now is that Swift currently requires RawValue be an integer, floating-point value, or string. I don’t know why the language has this restriction, so I can’t comment on how hard it would be to change.

- Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and allow `TupleName.caseName.propertyName` to access a tuple element without going through .rawValue.

Good point… Has there been a thread on allowing raw-valued enums to be treated as constants of type `RawValue` yet? Either way, removing the restriction on what types can be a RawValue is still my preferred solution.

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


(Vladimir) #15

Correct me if I'm wrong, but this idea with accessors is not the same as static properties for each case. The one of ideas of initial proposal - static(!) values would be created only once and it is important in case it is expensive to create such value(or if should be created only once per case)

The suggested solution based on 'accessor' - will create assotiated properties each time the enum instace created, for each instance of enum type.

We can have something like the example with accessors now :

enum MyError: ErrorProtocol {
     struct MyErrorInfo {
         let localizedFailureReason: String
         let url: String
     }

     case fileNotFound(url: String)
     case fileIsCorrupt(url: String)

     var info : MyErrorInfo {
         switch self {
             case fileNotFound(let url) : return MyErrorInfo(localizedFailureReason: "File \"\(url.lowercased())\" not found.", url: url)

             case fileIsCorrupt(let url) : return MyErrorInfo(localizedFailureReason: "File \"\(url.lowercased())\" is corrupt.", url: url)
         }
     }
}

var e = MyError.fileNotFound(url: "http://something.some")
var info = e.info
print(info.localizedFailureReason, info.url)

But yes, such MyErrorInfo will be created on each `info.` call. This is worse that create MyErrorInfo once per each enum instance initialization, but IMO these solutions are close enough.

In any case, I don't see why tuple for enum and enum with `accessor` can not co-exists.

···

On 27.05.2016 2:28, Charles Srstka via swift-evolution wrote:

On May 26, 2016, at 4:47 PM, Brent Royal-Gordon via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

- Abusing rawValue is just that: an abuse.

In addition, enums with associated types can’t have rawValues.

Why is this relevant, you may ask? Because error enums are a huge use case
for something like this. Being able to do the below would be great:

enum MyError: ErrorProtocol {
    accessor var localizedFailureReason: String
    accessor var url: NSURL

    case FileNotFound(url: NSURL) {
        self.localizedFailureReason = “File \"\(url.lastPathComponent ??
“”)\” not found.”
        self.url = url
    }

    case FileIsCorrupt(url: NSURL) {
        self.localizedFailureReason = “File \"\(url.lastPathComponent ??
“”)\” is corrupt.”
        self.url = url
    }
}

This would be much cleaner than the existing method of using a switch to
create a userInfo dictionary for creating an NSError to send to
-[NSApplication presentError:] and similar methods.

Charles

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


(Charlie Monroe) #16

What this proposal is asking for is an easier way to have derived values from enum cases. Asking for more flexible RawValues means mass and radius are not derived, they are the source of truth. It goes against the whole point of RawRepresentable. You are not saying ‘Mercury is identified by the case .mercury’, you are saying ‘Mercury is identified by a mass of 3.303e+23’. It’s backwards.

I see what Janis meant in the first email. It's not that the planet would be identified by the mass or radius. It could very much be

case Mercury = 1 where (mass: 3, radius: 2),

- Mercury's rawValue would be 1.

The issue here is that sometimes you want additional information with the enum. There are many cases where you extend the enum with a variable:

enum Error {
  case NoError
  case FileNotFound
  ...

  var isFatal: Bool {
    /// swtich over all values of self goes here.
  }

  var isNetworkError: Bool {
    /// swtich over all values of self goes here.
  }

  var isIOError: Bool {
    /// swtich over all values of self goes here.
  }
}

What the propsal suggests is to simplify this to the following:

enum Error {
  var isFatal: Bool

  case NoError where (isFatal: false, isNetworkError: false, isIOError: false)
  case FileNotFound where (isFatal: true, isNetworkError: false, isIOError: true)
  ...

}

So that you assign the additional information to the enum value itself.

Charlie

···

On 26 May 2016, at 1:47 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com <mailto:jtbandes@gmail.com>> wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it now is that Swift currently requires RawValue be an integer, floating-point value, or string. I don’t know why the language has this restriction, so I can’t comment on how hard it would be to change.

- Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and allow `TupleName.caseName.propertyName` to access a tuple element without going through .rawValue.

Good point… Has there been a thread on allowing raw-valued enums to be treated as constants of type `RawValue` yet? Either way, removing the restriction on what types can be a RawValue is still my preferred solution.

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Leonardo Pessoa) #17

That caseName I'd the solution I miss and would like to see. Indeed when I see an enum the name is the reference to whatever value it should be holding. Think of this:

// all relative to Earth's mass, I'm not digging the real values now
enum Mass : Float {
   case Earth = 1.0
   case Moon = 0.2
   ...
}

Since the values of each case are what's serialised, should I change the values here to absolute values I'm unable to deserialise the stored values because the values no longer exist.

IMO enums are a language resource used to mask values by a name just like a group of related constants. Being able to find an enum value by its raw value is a good way to convert the value into an enum but you should not rely on it to ever hold the same value forever so yes enum names are supposed to be the truth but that's just not how it works in Swift (still, I do love being able to parameterise enum cases, so I'm not suggesting to remove them).

As for having to use rawValue, we could work out a solution that would allow direct use of the properties in the tuple and I already have an idea: to make this:

enum Mass : Float {
   case Earth = 1.0
   case Moon = 0.2
}

be a shortcut in the language that is internally transformed and handled like this by the compiler:

enum Mass : (rawValue: Float) {
   case Earth = (rawValue : 1.0)
   case Moon = (rawValue: 0.2)
}

This doesn't break the current syntax of typed enums and still would allow us to access Planet.mercury.mass (in the previous examples) directly without using rawValue in the middle.

What do you think?

···

-----Original Message-----
From: "Jānis Kiršteins via swift-evolution" <swift-evolution@swift.org>
Sent: ‎26/‎05/‎2016 02:59 AM
To: "Charlie Monroe" <charlie@charliemonroe.net>
Cc: "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesforeach case

The argument against giving away raw value is that it grants
uniqueness of cases when serialized. One can reliably do:

// serialize
let rawValue = Planet.mercury.rawValue

// and de-serialize
guard let planet = Planet(rawValue: rawValue) else {
// ...
}

Currently raw values cannot only be equatables that are also literals
so their uniqueness can be checked at compile time. An alternative
could be that you can serialize/deserialize by case name. For example:

// serialize
let caseName = Planet.mercury.caseName // "mercury"

// de-serialize
guard let planet = Planet(caseName: "mercury") else {
// ...
}

On Thu, May 26, 2016 at 8:26 AM, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

What this proposal is asking for is an easier way to have derived values
from enum cases. Asking for more flexible RawValues means mass and radius
are not derived, they are the source of truth. It goes against the whole
point of RawRepresentable. You are not saying ‘Mercury is identified by the
case .mercury’, you are saying ‘Mercury is identified by a mass of
3.303e+23’. It’s backwards.

I see what Janis meant in the first email. It's not that the planet would be
identified by the mass or radius. It could very much be

case Mercury = 1 where (mass: 3, radius: 2),

- Mercury's rawValue would be 1.

The issue here is that sometimes you want additional information with the
enum. There are many cases where you extend the enum with a variable:

enum Error {
case NoError
case FileNotFound
...

var isFatal: Bool {
/// swtich over all values of self goes here.
}

var isNetworkError: Bool {
/// swtich over all values of self goes here.
}

var isIOError: Bool {
/// swtich over all values of self goes here.
}
}

What the propsal suggests is to simplify this to the following:

enum Error {
var isFatal: Bool

case NoError where (isFatal: false, isNetworkError: false, isIOError: false)
case FileNotFound where (isFatal: true, isNetworkError: false, isIOError:
true)
...

}

So that you assign the additional information to the enum value itself.

Charlie

On 26 May 2016, at 1:47 PM, David Sweeris via swift-evolution > <swift-evolution@swift.org> wrote:

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution > <swift-evolution@swift.org> wrote:

On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution >> <swift-evolution@swift.org> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it
now is that Swift currently requires RawValue be an integer, floating-point
value, or string. I don’t know why the language has this restriction, so I
can’t comment on how hard it would be to change.

- Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than
Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and
allow `TupleName.caseName.propertyName` to access a tuple element without
going through .rawValue.

Good point… Has there been a thread on allowing raw-valued enums to be
treated as constants of type `RawValue` yet? Either way, removing the
restriction on what types can be a RawValue is still my preferred solution.

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

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

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

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


(Jānis Kiršteins) #18

The argument against giving away raw value is that it grants
uniqueness of cases when serialized. One can reliably do:

// serialize
let rawValue = Planet.mercury.rawValue

// and de-serialize
guard let planet = Planet(rawValue: rawValue) else {
// ...
}

Currently raw values cannot only be equatables that are also literals
so their uniqueness can be checked at compile time. An alternative
could be that you can serialize/deserialize by case name. For example:

// serialize
let caseName = Planet.mercury.caseName // "mercury"

// de-serialize
guard let planet = Planet(caseName: "mercury") else {
// ...
}

···

On Thu, May 26, 2016 at 8:26 AM, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

What this proposal is asking for is an easier way to have derived values
from enum cases. Asking for more flexible RawValues means mass and radius
are not derived, they are the source of truth. It goes against the whole
point of RawRepresentable. You are not saying ‘Mercury is identified by the
case .mercury’, you are saying ‘Mercury is identified by a mass of
3.303e+23’. It’s backwards.

I see what Janis meant in the first email. It's not that the planet would be
identified by the mass or radius. It could very much be

case Mercury = 1 where (mass: 3, radius: 2),

- Mercury's rawValue would be 1.

The issue here is that sometimes you want additional information with the
enum. There are many cases where you extend the enum with a variable:

enum Error {
case NoError
case FileNotFound
...

var isFatal: Bool {
/// swtich over all values of self goes here.
}

var isNetworkError: Bool {
/// swtich over all values of self goes here.
}

var isIOError: Bool {
/// swtich over all values of self goes here.
}
}

What the propsal suggests is to simplify this to the following:

enum Error {
var isFatal: Bool

case NoError where (isFatal: false, isNetworkError: false, isIOError: false)
case FileNotFound where (isFatal: true, isNetworkError: false, isIOError:
true)
...

}

So that you assign the additional information to the enum value itself.

Charlie

On 26 May 2016, at 1:47 PM, David Sweeris via swift-evolution > <swift-evolution@swift.org> wrote:

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com> > wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution > <swift-evolution@swift.org> wrote:

On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution >> <swift-evolution@swift.org> wrote:

Hi,

Couldn't this be solved by using tuples? If not because the syntax is not
allowed I think this would be more coherent to do it using current syntax.

enum Planet : (mass: Float, radius: Float) {
    case mercury = (mass: 3.303e+23, radius: 2.4397e6)
    case venus = (mass: 4.869e+24, radius: 6.0518e6)
    case earth = (mass: 5.976e+24, radius: 6.37814e6)
    case mars = (mass: 6.421e+23, radius: 3.3972e6)
    case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
    case saturn = (mass: 5.688e+26, radius: 6.0268e7)
    case uranus = (mass: 8.686e+25, radius: 2.5559e7)
    case neptune = (mass: 1.024e+26, radius: 2.4746e7)
}

This would be my preferred solution… AFAIK, the only reason we can’t do it
now is that Swift currently requires RawValue be an integer, floating-point
value, or string. I don’t know why the language has this restriction, so I
can’t comment on how hard it would be to change.

- Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than
Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues, and
allow `TupleName.caseName.propertyName` to access a tuple element without
going through .rawValue.

Good point… Has there been a thread on allowing raw-valued enums to be
treated as constants of type `RawValue` yet? Either way, removing the
restriction on what types can be a RawValue is still my preferred solution.

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

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

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


(Vladimir) #19

I support the proposal, but couldn't the initial target be achieved today with such (more verbose,yes) solution? :

enum Planet {
     struct PlanetInfo {
         var mass: Double
         var description: String
     }

     case earth
     case moon

     var info : PlanetInfo {
         switch self {
             case earth: return PlanetInfo(mass: 1.0, description: "Earth is our home")
             case moon: return PlanetInfo(mass: 0.2, description: "Just a moon")
         }
     }
}

let e = Planet.earth
print(e, e.info.description)

let m = Planet.moon
print(m, m.info.description)

···

On 26.05.2016 8:26, Charlie Monroe via swift-evolution wrote:

What this proposal is asking for is an easier way to have derived values
from enum cases. Asking for more flexible RawValues means mass and radius
are not derived, they are the source of truth. It goes against the whole
point of RawRepresentable. You are not saying ‘Mercury is identified by
the case .mercury’, you are saying ‘Mercury is identified by a mass of
3.303e+23’. It’s backwards.

I see what Janis meant in the first email. It's not that the planet would
be identified by the mass or radius. It could very much be

case Mercury = 1 where (mass: 3, radius: 2),

- Mercury's rawValue would be 1.

The issue here is that sometimes you want additional information with the
enum. There are many cases where you extend the enum with a variable:

enum Error {
case NoError
case FileNotFound
...

var isFatal: Bool {
/// swtich over all values of self goes here.
}

var isNetworkError: Bool {
/// swtich over all values of self goes here.
}

var isIOError: Bool {
/// swtich over all values of self goes here.
}

What the propsal suggests is to simplify this to the following:

enum Error {
var isFatal: Bool

case NoError where (isFatal: false, isNetworkError: false, isIOError: false)
case FileNotFound where (isFatal: true, isNetworkError: false, isIOError:
true)
...

}

So that you assign the additional information to the enum value itself.

Charlie

On 26 May 2016, at 1:47 PM, David Sweeris via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 25, 2016, at 10:27 PM, Jacob Bandes-Storch <jtbandes@gmail.com >>>> <mailto:jtbandes@gmail.com>> wrote:

On Wed, May 25, 2016 at 8:15 PM, David Sweeris via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    On May 25, 2016, at 7:37 AM, Leonardo Pessoa via swift-evolution >>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Hi,

    Couldn't this be solved by using tuples? If not because the syntax
    is not allowed I think this would be more coherent to do it using
    current syntax.

    enum Planet : (mass: Float, radius: Float) {
        case mercury = (mass: 3.303e+23, radius: 2.4397e6)
        case venus = (mass: 4.869e+24, radius: 6.0518e6)
        case earth = (mass: 5.976e+24, radius: 6.37814e6)
        case mars = (mass: 6.421e+23, radius: 3.3972e6)
        case jupiter = (mass: 1.9e+27, radius: 7.1492e7)
        case saturn = (mass: 5.688e+26, radius: 6.0268e7)
        case uranus = (mass: 8.686e+25, radius: 2.5559e7)
        case neptune = (mass: 1.024e+26, radius: 2.4746e7)
    }

    This would be my preferred solution… AFAIK, the only reason we
    can’t do it now is that Swift currently requires RawValue be an
    integer, floating-point value, or string. I don’t know why the
    language has this restriction, so I can’t comment on how hard it
    would be to change.

    - Dave Sweeris

Except you'd have to write Planet.mercury.rawValue.mass, rather than
Planet.mercury.mass.

This could be one or two proposals: allow enums with tuple RawValues,
and allow `TupleName.caseName.propertyName` to access a tuple element
without going through .rawValue.

Good point… Has there been a thread on allowing raw-valued enums to be
treated as constants of type `RawValue` yet? Either way, removing the
restriction on what types can be a RawValue is still my preferred solution.

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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


(Brent Royal-Gordon) #20

The suggested solution based on 'accessor' - will create assotiated properties each time the enum instace created, for each instance of enum type.

No; property accessors would be either computed or constant (so that all instances of a given case can share storage). This is much the way they would behave if they were included in behaviors.

You could write a property accessor with a setter, but it would have to be computed, and manipulate `self`'s cases and associated values:

  enum Optional<T> {
    accessor var unwrapped: T { get set }

    case none {
      unwrapped {
        get { fatalError("No value") }
        set { self = .some(newValue) }
      }
    }
    case some (_ value: T) {
      unwrapped {
        get { return value }
        set { self = .some(newValue) }
      }
    }
  }

···

--
Brent Royal-Gordon
Architechies