[Proposal] Enums with static stored properties for each case

I agree. These are different proposals that can coexist but I think they should be evaluated separately by the community.

···

-----Original Message-----
From: "Vladimir.S via swift-evolution" <swift-evolution@swift.org>
Sent: ‎27/‎05/‎2016 08:05 AM
To: "Charles Srstka" <cocoadev@charlessoft.com>
Cc: "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesfor each case

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

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

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the 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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

Property accessors might work for enums with associated values, but
not so well without them.

···

On Fri, May 27, 2016 at 3:43 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

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

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

The problem is that PlanetInfo values are recreated each time while
they are static. Imagine if PlanetInfo where some type that expensive
to create performance wise.

You could solve it by:

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

    case earth
    case moon

    private static earthInfo = PlanetInfo(mass: 1.0, description:
"Earth is our home")
    private static moonInfo = PlanetInfo(mass: 0.2, description: "Just a moon")

    var info : PlanetInfo {
        switch self {
            case earth: return PlanetInfo.earthInfo
            case moon: return PlanetInfo.moonInfo
        }
    }
}

But that again more verbose. The proposed solution is explicit that
those properties are static for each case.

···

On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

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

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

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

  struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
    private var dictionary: [Key: Value]
    
    init(dictionaryLiteral elements: (Key, Value)...) {
      dictionary = [:]
      for (k, v) in elements {
        dictionary[k] = v
      }
      
      if dictionary.count != Key.allValues.count {
        let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
        preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
      }
    }
    
    var startIndex: Dictionary.Index {
      return dictionary.startIndex
    }
    var endIndex: Dictionary.Index {
      return dictionary.endIndex
    }
    subscript(index: Dictionary.Index) -> (Key, Value) {
      return dictionary[index]
    }
    func index(after i: Dictionary.Index) -> Dictionary.Index {
      return dictionary.index(after: i)
    }

    subscript(key: Key) -> Value {
      get { return dictionary[key]! }
      set { dictionary[key] = newValue }
    }
  }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

···

--
Brent Royal-Gordon
Architechies

My suggestion of allowing tuples as raw values instead doesn't burden the language and also does not eliminate rawValue (treat previously supported raw value types as one value tuples), reads very cleanly and supports a syntax we're already familiar with. I don't see how the 'where' syntax reads like natural language and I agree it doesn't match other uses of where in the language.

···

-----Original Message-----
From: "Jānis Kiršteins via swift-evolution" <swift-evolution@swift.org>
Sent: ‎28/‎05/‎2016 10:54 AM
To: "Brent Royal-Gordon" <brent@architechies.com>
Cc: "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesfor each case

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the 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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

Property accessors might work for enums with associated values, but
not so well without them.

On Fri, May 27, 2016 at 3:43 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

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

_______________________________________________
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

As previosly state there is one problem using tuples as rawValues - they currently must be static (literals) and unique. In example with planets the mass or radius is not unique so it cannot be used as raw value with current requirments.

···

On 28 May 2016, at 20:22, Leonardo Pessoa <me@lmpessoa.com> wrote:

My suggestion of allowing tuples as raw values instead doesn't burden the language and also does not eliminate rawValue (treat previously supported raw value types as one value tuples), reads very cleanly and supports a syntax we're already familiar with. I don't see how the 'where' syntax reads like natural language and I agree it doesn't match other uses of where in the language.

From: Jānis Kiršteins via swift-evolution
Sent: ‎28/‎05/‎2016 10:54 AM
To: Brent Royal-Gordon
Cc: swift-evolution
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesfor each case

> - Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the 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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

Property accessors might work for enums with associated values, but
not so well without them.

On Fri, May 27, 2016 at 3:43 PM, Brent Royal-Gordon via > swift-evolution <swift-evolution@swift.org> wrote:
>> 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
>
> _______________________________________________
> 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

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

···

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

        struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
                private var dictionary: [Key: Value]

                init(dictionaryLiteral elements: (Key, Value)...) {
                        dictionary = [:]
                        for (k, v) in elements {
                                dictionary[k] = v
                        }

                        if dictionary.count != Key.allValues.count {
                                let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                                preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                        }
                }

                var startIndex: Dictionary.Index {
                        return dictionary.startIndex
                }
                var endIndex: Dictionary.Index {
                        return dictionary.endIndex
                }
                subscript(index: Dictionary.Index) -> (Key, Value) {
                        return dictionary[index]
                }
                func index(after i: Dictionary.Index) -> Dictionary.Index {
                        return dictionary.index(after: i)
                }

                subscript(key: Key) -> Value {
                        get { return dictionary[key]! }
                        set { dictionary[key] = newValue }
                }
        }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

Or(if we are sure we'll don't forget to udpate `infoDict` in case of new added case in future):

enum Planet {
     case earth
     case moon

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

     private static let infoDict = [
         Planet.earth :
             PlanetInfo(mass: 1.0, description:"Earth is our home"),
         .moon:
             PlanetInfo(mass: 0.2, description:"Just a moon"),
         ]

     var info : PlanetInfo { return Planet.infoDict[self]! }
}

But I agree with you, IMO we need static stored properties for each case.

···

On 26.05.2016 18:15, Jānis Kiršteins wrote:

The problem is that PlanetInfo values are recreated each time while
they are static. Imagine if PlanetInfo where some type that expensive
to create performance wise.

You could solve it by:

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

    case earth
    case moon

    private static earthInfo = PlanetInfo(mass: 1.0, description:
"Earth is our home")
    private static moonInfo = PlanetInfo(mass: 0.2, description: "Just a moon")

    var info : PlanetInfo {
        switch self {
            case earth: return PlanetInfo.earthInfo
            case moon: return PlanetInfo.moonInfo
        }
    }
}

But that again more verbose. The proposed solution is explicit that
those properties are static for each case.

On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution > <swift-evolution@swift.org> wrote:

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

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

So it should either be an exception when using tuples (which I know is not good) or ensure the combination of values in all tuple values are different from one another.

···

-----Original Message-----
From: "Jānis Kiršteins" <janis.kirsteins@gmail.com>
Sent: ‎28/‎05/‎2016 03:58 PM
To: "Leonardo Pessoa" <me@lmpessoa.com>
Cc: "Brent Royal-Gordon" <brent@architechies.com>; "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static storedpropertiesfor each case

As previosly state there is one problem using tuples as rawValues - they currently must be static (literals) and unique. In example with planets the mass or radius is not unique so it cannot be used as raw value with current requirments.

On 28 May 2016, at 20:22, Leonardo Pessoa <me@lmpessoa.com> wrote:

My suggestion of allowing tuples as raw values instead doesn't burden the language and also does not eliminate rawValue (treat previously supported raw value types as one value tuples), reads very cleanly and supports a syntax we're already familiar with. I don't see how the 'where' syntax reads like natural language and I agree it doesn't match other uses of where in the language.

From: Jānis Kiršteins via swift-evolution
Sent: ‎28/‎05/‎2016 10:54 AM
To: Brent Royal-Gordon
Cc: swift-evolution
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesfor each case

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the 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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

Property accessors might work for enums with associated values, but
not so well without them.

On Fri, May 27, 2016 at 3:43 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

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

_______________________________________________
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

I think that's the case with enums. You're changing their current behaviour of only having stored values to one in which it's computed (even if only once and then stored). Enums are IMO something that have a static value you know beforehand and can count on. That's why I'm not fond of the accessor proposal. Otherwise I think we're transforming enums into a closed set of struct instances and one could do that already by using a private init.

···

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon > <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

       struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
               private var dictionary: [Key: Value]

               init(dictionaryLiteral elements: (Key, Value)...) {
                       dictionary = [:]
                       for (k, v) in elements {
                               dictionary[k] = v
                       }

                       if dictionary.count != Key.allValues.count {
                               let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                               preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                       }
               }

               var startIndex: Dictionary.Index {
                       return dictionary.startIndex
               }
               var endIndex: Dictionary.Index {
                       return dictionary.endIndex
               }
               subscript(index: Dictionary.Index) -> (Key, Value) {
                       return dictionary[index]
               }
               func index(after i: Dictionary.Index) -> Dictionary.Index {
                       return dictionary.index(after: i)
               }

               subscript(key: Key) -> Value {
                       get { return dictionary[key]! }
                       set { dictionary[key] = newValue }
               }
       }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

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

Perhaps there's an argument to be made for a sort of 'enumDictionary' type
- a dictionary whose keys are all the cases of an enum, and is thus
guaranteed to produce a value.

I think the question I have is how you'd access the values, syntactically.
To use the Planet example, if '.earth' is a value of the Planet enum, is
'.earth.mass' an acceptable way to access its mass? Or perhaps
'Planet[.earth].mass'?

···

On Thu, May 26, 2016 at 4:43 PM, Vladimir.S via swift-evolution < swift-evolution@swift.org> wrote:

Or(if we are sure we'll don't forget to udpate `infoDict` in case of new
added case in future):

enum Planet {
    case earth
    case moon

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

    private static let infoDict = [
        Planet.earth :
            PlanetInfo(mass: 1.0, description:"Earth is our home"),
        .moon:
            PlanetInfo(mass: 0.2, description:"Just a moon"),
        ]

    var info : PlanetInfo { return Planet.infoDict[self]! }
}

But I agree with you, IMO we need static stored properties for each case.

On 26.05.2016 18:15, Jānis Kiršteins wrote:

The problem is that PlanetInfo values are recreated each time while
they are static. Imagine if PlanetInfo where some type that expensive
to create performance wise.

You could solve it by:

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

    case earth
    case moon

    private static earthInfo = PlanetInfo(mass: 1.0, description:
"Earth is our home")
    private static moonInfo = PlanetInfo(mass: 0.2, description: "Just a
moon")

    var info : PlanetInfo {
        switch self {
            case earth: return PlanetInfo.earthInfo
            case moon: return PlanetInfo.moonInfo
        }
    }
}

But that again more verbose. The proposed solution is explicit that
those properties are static for each case.

On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org> wrote:

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

_______________________________________________

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

Perhaps there's an argument to be made for a sort of 'enumDictionary' type
- a dictionary whose keys are all the cases of an enum, and is thus
guaranteed to produce a value.

In Delphi(Pascal) you can define an array with indexes of enum type i.e.:
type
   TMyEnum = (One, Two)
var
   MyVal : array[TMyEnum] of String
const
   MyConsts : array [TMyEnum] of String = ('just one', 'two here')
   // compiler will check that values for each enum were specified here

,so you can do
var e: TMyEnum
e := One;
MyVal[e] := 'hello';
s2 := MyConsts[e];

This is really useful and used a lot. And this is safe in meaning compiler will notify you if you changed the enum - you'll have to change such constant array.

I wish we'll have something like this in Swift.

I think the question I have is how you'd access the values, syntactically.
To use the Planet example, if '.earth' is a value of the Planet enum, is
'.earth.mass' an acceptable way to access its mass? Or perhaps
'Planet[.earth].mass'?

Just like .rawValue currently, i.e.
let e = Planet.earth
print(e.mass, e.description)

···

On 26.05.2016 19:50, Ross O'Brien wrote:

On Thu, May 26, 2016 at 4:43 PM, Vladimir.S via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Or(if we are sure we'll don't forget to udpate `infoDict` in case of
    new added case in future):

    enum Planet {
        case earth
        case moon

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

        private static let infoDict = [
            Planet.earth :
                PlanetInfo(mass: 1.0, description:"Earth is our home"),
            .moon:
                PlanetInfo(mass: 0.2, description:"Just a moon"),
            ]

        var info : PlanetInfo { return Planet.infoDict[self]! }
    }

    But I agree with you, IMO we need static stored properties for each case.

    On 26.05.2016 18 <tel:26.05.2016%2018>:15, Jānis Kiršteins wrote:

        The problem is that PlanetInfo values are recreated each time while
        they are static. Imagine if PlanetInfo where some type that expensive
        to create performance wise.

        You could solve it by:

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

            case earth
            case moon

            private static earthInfo = PlanetInfo(mass: 1.0, description:
        "Earth is our home")
            private static moonInfo = PlanetInfo(mass: 0.2, description:
        "Just a moon")

            var info : PlanetInfo {
                switch self {
                    case earth: return PlanetInfo.earthInfo
                    case moon: return PlanetInfo.moonInfo
                }
            }
        }

        But that again more verbose. The proposed solution is explicit that
        those properties are static for each case.

        On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

            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>
                        <mailto: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> > <mailto: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>
                            <mailto: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>
                            <mailto: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>
                        <mailto: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>
                    <mailto: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 <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

I wrote a proposal draft:

# Enum case stored properties

* Proposal: TBD
* Author: [Janis Kirsteins](https://github.com/kirsteins\)
* Status: TBD
* Review manager: TBD

## Introduction

This proposal allows each enum case to have stored properties.

## Motivation

Enums cases can have a lot of constant (or variable) static values
associated with it. For example, planets can have mass, radius, age,
closest star etc. Currently there is no way to set or get those values
easily.

Example below shows that is hard to read and manage static associated
values with each case. It is hard to add or remove case as it would
require to add or remove code in four different places in file. Also
static associated value like `UIBezierPath` is recreated each time the
property is computed while it's constant.

enum Suit {
    case spades
    case hearts
    case diamonds
    case clubs

    var simpleDescription: String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }

    var color: UIColor {
        switch self {
        case .spades:
            return .blackColor()
        case .hearts:
            return .redColor()
        case .diamonds:
            return .redColor()
        case .clubs:
            return .blackColor()
        }
    }

    var symbol: String {
        switch self {
        case .spades:
            return "♠"
        case .hearts:
            return "♥"
        case .diamonds:
            return "♦"
        case .clubs:
            return "♣"
        }
    }

    var bezierPath: UIBezierPath {
        switch self {
        case .spades:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .hearts:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .diamonds:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .clubs:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        }
    }
}

## Proposed solution

Support stored properties for enum cases just as each case were an
instance. Case properties are initialized block after each case
declaration.

enum Suit {
    let simpleDescription: String
    let color: UIColor
    let symbol: String
    let bezierPath: UIBezierPath

    case spades {
        simpleDescription = "spades"
        color = .blackColor()
        symbol = "♠"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case hearts {
        simpleDescription = "hearts"
        color = .redColor()
        symbol = "♥"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case diamonds {
        simpleDescription = "diamonds"
        color = .redColor()
        symbol = "♦"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case clubs {
        simpleDescription = "clubs"
        color = .blackColor()
        symbol = "♣"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }
}

let symbol = Suit.spades.symbol // "♠"

The proposed solution improves:
- Readability as cases are closer with their related data;
- Improves code maintainability as a case can be removed or added in one place;
- Improved performance as there is no need to recreate static values;
- ~30% less lines of code in given example.

## Detailed design

#### Stored properties

Enum stored properties are supported the same way they are supported
for structs can classes. Unlike enum associated values, stored
properties are static to case and are shared for the same case.

Properties are accessed:

let simpleDescription = Suit.spades.simpleDescription

Mutable properties can be set:

Suit.spades.simpleDescription = "new simple description"

#### Initialization

If enum has uninitialized stored property it must be initialized in a
block after each case declaration. The block work the same way as
struct initialization. At the end of initialization block all
properties must be initialized.

enum Suit {
    var simpleDescription: String

    case spades {
        simpleDescription = "spades"
    }
}

Initialization block can be combine with use of `rawValue`:

enum Suit: Int {
    var simpleDescription: String

    case spades = 1 {
        simpleDescription = "spades"
    }
}

or associated values of the case:

enum Suit {
    var simpleDescription: String

    case spades(Int) {
        simpleDescription = "spades"
    }
}

## Impact on existing code

Stored properties for enums are not currently not supported, so there
is no impact on existing code.

## Alternatives considered

- Use labeled tuple as `rawValue` of the enum case. This approach is
not compatible as it conflicts with intention of `rawValue` of Swift
enum;
- Use per case initializer like [Java
Enum](JDK 21 Documentation - Home).
Swift enum uses custom initializer syntax to setup instances, not
cases. So this approach is not suitable for Swift.

···

On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think that's the case with enums. You're changing their current behaviour of only having stored values to one in which it's computed (even if only once and then stored). Enums are IMO something that have a static value you know beforehand and can count on. That's why I'm not fond of the accessor proposal. Otherwise I think we're transforming enums into a closed set of struct instances and one could do that already by using a private init.

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >> <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

       struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
               private var dictionary: [Key: Value]

               init(dictionaryLiteral elements: (Key, Value)...) {
                       dictionary = [:]
                       for (k, v) in elements {
                               dictionary[k] = v
                       }

                       if dictionary.count != Key.allValues.count {
                               let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                               preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                       }
               }

               var startIndex: Dictionary.Index {
                       return dictionary.startIndex
               }
               var endIndex: Dictionary.Index {
                       return dictionary.endIndex
               }
               subscript(index: Dictionary.Index) -> (Key, Value) {
                       return dictionary[index]
               }
               func index(after i: Dictionary.Index) -> Dictionary.Index {
                       return dictionary.index(after: i)
               }

               subscript(key: Key) -> Value {
                       get { return dictionary[key]! }
                       set { dictionary[key] = newValue }
               }
       }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

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

Java enums automatically have a static values() method that return an array with all values in an enum.

···

-----Original Message-----
From: "Vladimir.S via swift-evolution" <swift-evolution@swift.org>
Sent: ‎26/‎05/‎2016 02:36 PM
To: "Ross O'Brien" <narrativium+swift@gmail.com>
Cc: "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesforeach case

On 26.05.2016 19:50, Ross O'Brien wrote:

Perhaps there's an argument to be made for a sort of 'enumDictionary' type
- a dictionary whose keys are all the cases of an enum, and is thus
guaranteed to produce a value.

In Delphi(Pascal) you can define an array with indexes of enum type i.e.:
type
   TMyEnum = (One, Two)
var
   MyVal : array[TMyEnum] of String
const
   MyConsts : array [TMyEnum] of String = ('just one', 'two here')
   // compiler will check that values for each enum were specified here

,so you can do
var e: TMyEnum
e := One;
MyVal[e] := 'hello';
s2 := MyConsts[e];

This is really useful and used a lot. And this is safe in meaning compiler
will notify you if you changed the enum - you'll have to change such
constant array.

I wish we'll have something like this in Swift.

I think the question I have is how you'd access the values, syntactically.
To use the Planet example, if '.earth' is a value of the Planet enum, is
'.earth.mass' an acceptable way to access its mass? Or perhaps
'Planet[.earth].mass'?

Just like .rawValue currently, i.e.
let e = Planet.earth
print(e.mass, e.description)

On Thu, May 26, 2016 at 4:43 PM, Vladimir.S via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Or(if we are sure we'll don't forget to udpate `infoDict` in case of
    new added case in future):

    enum Planet {
        case earth
        case moon

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

        private static let infoDict = [
            Planet.earth :
                PlanetInfo(mass: 1.0, description:"Earth is our home"),
            .moon:
                PlanetInfo(mass: 0.2, description:"Just a moon"),
            ]

        var info : PlanetInfo { return Planet.infoDict[self]! }
    }

    But I agree with you, IMO we need static stored properties for each case.

    On 26.05.2016 18 <tel:26.05.2016%2018>:15, Jānis Kiršteins wrote:

        The problem is that PlanetInfo values are recreated each time while
        they are static. Imagine if PlanetInfo where some type that expensive
        to create performance wise.

        You could solve it by:

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

            case earth
            case moon

            private static earthInfo = PlanetInfo(mass: 1.0, description:
        "Earth is our home")
            private static moonInfo = PlanetInfo(mass: 0.2, description:
        "Just a moon")

            var info : PlanetInfo {
                switch self {
                    case earth: return PlanetInfo.earthInfo
                    case moon: return PlanetInfo.moonInfo
                }
            }
        }

        But that again more verbose. The proposed solution is explicit that
        those properties are static for each case.

        On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

            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>
                        <mailto: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> > <mailto: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>
                            <mailto: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>
                            <mailto: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>
                        <mailto: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>
                    <mailto: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 <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

Yes, this was mentioned in a similar thread in this email list earlier. There is even some proposal for such .values() for Swift enums.

But this values() in Java is not the same thing as discussed dictionary with *keys* of enum type or Delphi's arrays with *index* of enum type.

Could you write Java's example for array/dictionary of String which *index*(or key) will be of enum type? *And* compiler will check that value for each enum case is set in case of array of constants like:
MyConsts : array [TMyEnum] of String = ('just one', 'two here')
// compiler will always check that value assigned for each case

···

On 26.05.2016 20:58, Leonardo Pessoa wrote:

Java enums automatically have a static values() method that return an array
with all values in an enum.
---------------------------------------------------------------------------
From: Vladimir.S via swift-evolution <mailto:swift-evolution@swift.org>
Sent: ‎26/‎05/‎2016 02:36 PM
To: Ross O'Brien <mailto:narrativium+swift@gmail.com>
Cc: swift-evolution <mailto:swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored
propertiesforeach case

On 26.05.2016 19:50, Ross O'Brien wrote:

Perhaps there's an argument to be made for a sort of 'enumDictionary' type
- a dictionary whose keys are all the cases of an enum, and is thus
guaranteed to produce a value.

In Delphi(Pascal) you can define an array with indexes of enum type i.e.:
type
   TMyEnum = (One, Two)
var
   MyVal : array[TMyEnum] of String
const
   MyConsts : array [TMyEnum] of String = ('just one', 'two here')
   // compiler will check that values for each enum were specified here

,so you can do
var e: TMyEnum
e := One;
MyVal[e] := 'hello';
s2 := MyConsts[e];

This is really useful and used a lot. And this is safe in meaning compiler
will notify you if you changed the enum - you'll have to change such
constant array.

I wish we'll have something like this in Swift.

I think the question I have is how you'd access the values, syntactically.
To use the Planet example, if '.earth' is a value of the Planet enum, is
'.earth.mass' an acceptable way to access its mass? Or perhaps
'Planet[.earth].mass'?

Just like .rawValue currently, i.e.
let e = Planet.earth
print(e.mass, e.description)

On Thu, May 26, 2016 at 4:43 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    Or(if we are sure we'll don't forget to udpate `infoDict` in case of
    new added case in future):

    enum Planet {
        case earth
        case moon

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

        private static let infoDict = [
            Planet.earth :
                PlanetInfo(mass: 1.0, description:"Earth is our home"),
            .moon:
                PlanetInfo(mass: 0.2, description:"Just a moon"),
            ]

        var info : PlanetInfo { return Planet.infoDict[self]! }
    }

    But I agree with you, IMO we need static stored properties for each case.

    On 26.05.2016 18 <tel:26.05.2016%2018>:15, Jānis Kiršteins wrote:

        The problem is that PlanetInfo values are recreated each time while
        they are static. Imagine if PlanetInfo where some type that expensive
        to create performance wise.

        You could solve it by:

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

            case earth
            case moon

            private static earthInfo = PlanetInfo(mass: 1.0, description:
        "Earth is our home")
            private static moonInfo = PlanetInfo(mass: 0.2, description:
        "Just a moon")

            var info : PlanetInfo {
                switch self {
                    case earth: return PlanetInfo.earthInfo
                    case moon: return PlanetInfo.moonInfo
                }
            }
        }

        But that again more verbose. The proposed solution is explicit that
        those properties are static for each case.

        On Thu, May 26, 2016 at 5:58 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

            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>
                        <mailto: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> >> <mailto: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>
                            <mailto: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>
                            <mailto: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>
                        <mailto: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>
                    <mailto: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 <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

As I said before, I'm not in favour of this approach. And you completely missed my proposal in the alternatives.

···

-----Original Message-----
From: "Jānis Kiršteins" <janis.kirsteins@gmail.com>
Sent: ‎31/‎05/‎2016 11:17 AM
To: "Leonardo Pessoa" <me@lmpessoa.com>
Cc: "Brent Royal-Gordon" <brent@architechies.com>; "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Enums with static stored propertiesfor each case

I wrote a proposal draft:

# Enum case stored properties

* Proposal: TBD
* Author: [Janis Kirsteins](https://github.com/kirsteins\)
* Status: TBD
* Review manager: TBD

## Introduction

This proposal allows each enum case to have stored properties.

## Motivation

Enums cases can have a lot of constant (or variable) static values
associated with it. For example, planets can have mass, radius, age,
closest star etc. Currently there is no way to set or get those values
easily.

Example below shows that is hard to read and manage static associated
values with each case. It is hard to add or remove case as it would
require to add or remove code in four different places in file. Also
static associated value like `UIBezierPath` is recreated each time the
property is computed while it's constant.

enum Suit {
    case spades
    case hearts
    case diamonds
    case clubs

    var simpleDescription: String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }

    var color: UIColor {
        switch self {
        case .spades:
            return .blackColor()
        case .hearts:
            return .redColor()
        case .diamonds:
            return .redColor()
        case .clubs:
            return .blackColor()
        }
    }

    var symbol: String {
        switch self {
        case .spades:
            return "♠"
        case .hearts:
            return "♥"
        case .diamonds:
            return "♦"
        case .clubs:
            return "♣"
        }
    }

    var bezierPath: UIBezierPath {
        switch self {
        case .spades:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .hearts:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .diamonds:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .clubs:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        }
    }
}

## Proposed solution

Support stored properties for enum cases just as each case were an
instance. Case properties are initialized block after each case
declaration.

enum Suit {
    let simpleDescription: String
    let color: UIColor
    let symbol: String
    let bezierPath: UIBezierPath

    case spades {
        simpleDescription = "spades"
        color = .blackColor()
        symbol = "♠"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case hearts {
        simpleDescription = "hearts"
        color = .redColor()
        symbol = "♥"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case diamonds {
        simpleDescription = "diamonds"
        color = .redColor()
        symbol = "♦"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case clubs {
        simpleDescription = "clubs"
        color = .blackColor()
        symbol = "♣"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }
}

let symbol = Suit.spades.symbol // "♠"

The proposed solution improves:
- Readability as cases are closer with their related data;
- Improves code maintainability as a case can be removed or added in one place;
- Improved performance as there is no need to recreate static values;
- ~30% less lines of code in given example.

## Detailed design

#### Stored properties

Enum stored properties are supported the same way they are supported
for structs can classes. Unlike enum associated values, stored
properties are static to case and are shared for the same case.

Properties are accessed:

let simpleDescription = Suit.spades.simpleDescription

Mutable properties can be set:

Suit.spades.simpleDescription = "new simple description"

#### Initialization

If enum has uninitialized stored property it must be initialized in a
block after each case declaration. The block work the same way as
struct initialization. At the end of initialization block all
properties must be initialized.

enum Suit {
    var simpleDescription: String

    case spades {
        simpleDescription = "spades"
    }
}

Initialization block can be combine with use of `rawValue`:

enum Suit: Int {
    var simpleDescription: String

    case spades = 1 {
        simpleDescription = "spades"
    }
}

or associated values of the case:

enum Suit {
    var simpleDescription: String

    case spades(Int) {
        simpleDescription = "spades"
    }
}

## Impact on existing code

Stored properties for enums are not currently not supported, so there
is no impact on existing code.

## Alternatives considered

- Use labeled tuple as `rawValue` of the enum case. This approach is
not compatible as it conflicts with intention of `rawValue` of Swift
enum;
- Use per case initializer like [Java
Enum](JDK 21 Documentation - Home).
Swift enum uses custom initializer syntax to setup instances, not
cases. So this approach is not suitable for Swift.

On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think that's the case with enums. You're changing their current behaviour of only having stored values to one in which it's computed (even if only once and then stored). Enums are IMO something that have a static value you know beforehand and can count on. That's why I'm not fond of the accessor proposal. Otherwise I think we're transforming enums into a closed set of struct instances and one could do that already by using a private init.

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >> <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

       struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
               private var dictionary: [Key: Value]

               init(dictionaryLiteral elements: (Key, Value)...) {
                       dictionary = [:]
                       for (k, v) in elements {
                               dictionary[k] = v
                       }

                       if dictionary.count != Key.allValues.count {
                               let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                               preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                       }
               }

               var startIndex: Dictionary.Index {
                       return dictionary.startIndex
               }
               var endIndex: Dictionary.Index {
                       return dictionary.endIndex
               }
               subscript(index: Dictionary.Index) -> (Key, Value) {
                       return dictionary[index]
               }
               func index(after i: Dictionary.Index) -> Dictionary.Index {
                       return dictionary.index(after: i)
               }

               subscript(key: Key) -> Value {
                       get { return dictionary[key]! }
                       set { dictionary[key] = newValue }
               }
       }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

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

Just complementing as I was on the run when I saw and responded the
previous message.

In my proposal, we would be allowing tuples to be used as if they were
value types in enums.

  enum Planet : (mass: Double, radius: Double) {
      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)
  }

Since tuples cannot be tested for uniqueness (I'd even go further to
say this would be undesireable here), you would also not have the
init(rawValue:) method testing for tuples and thus you would not have
the rawValue property of single typed enums returning the tuple
either, just like non-typed enums (calling .rawValue on a non-typed
enum will return the enum value itself, thus calling
Planet.Mercury.rawValue would return .Mercury itself here too). This
imposes no changes on existing code and allows for the values to be
refered to directly without using .rawValue in the middle (e.g.
print(Planet.Mercury.radius) instead of
print(Planet.Mercury.rawValue.radius)). You could even think of
internally representing single typed enums using a single value tuple
like the following:

  enum Order : Int {
      case First = 1
      case Second
      case Third
      case Fourth
  }

could be still declared like this but be internally represented as if it were:

  enum Order : (rawValue: Int) {
      case First = (rawValue: 1)
      case Second = (rawValue: 2)
      case Third = (rawValue: 3)
      case Fourth = (rawValue: 4)
  }

But that's just a simplification for internal representation and may
not even be used (I think this will be left for the Apple team to
decide how it should be implemented, either way current raw values
won't be broken).

IMO, this proposal only expands the possible set of values a typed
enum can hold using a syntax that is as close to the current one as I
think possible. Enums are, as defined in the Swift documentation, "a
common type for a group of related **values**" afterall and it makes
no sense to me to associate computed properties (which could return
different values each time they're called) to store constant value
properties and it's also much more verbose. Tuples can do this job
much better, cleaner and efficiently. You may also remember enums with
associated values: their values are defined when the enum is
initialised and never change either; you may create a new instance
with new values derived from another instance but you can never change
the values of an instance. Thus allowing an enum value to hold a
computed property seem like we're changing enums into something else.

Just one more idea to bring to this proposal, I thought there could be
a way to still have the init(rawValue:) method using tuples by simply
declaring one of the values of the tuple to be rawValue itself. The
compiler would enforce the uniqueness of values in this field and the
initialiser could find the enum value by its raw value, like this:

  enum Planet : (mass: Double, radius: Double, rawValue: Int) {
     case Mercury = (mass: 3.303e+23, radius: 2.4397e6, rawValue: 1)
     case Venus = (mass: 4.869e+24, radius: 6.0518e6, rawValue: 2)
     case Earth = (mass: 5.976e+24, radius: 6.37814e6, rawValue: 3)
     case Mars = (mass: 6.421e+23, radius: 3.3972e6, rawValue: 4)
     case Jupiter = (mass: 1.9e+27, radius: 7.1492e7, rawValue: 5)
     case Saturn = (mass: 5.688e+26, radius: 6.0268e7, rawValue: 6)
     case Uranus = (mass: 8.686e+25, radius: 2.5559e7, rawValue: 7)
     case Neptune = (mass: 1.024e+26, radius: 2.4746e7, rawValue: 8)
  }

and thus allow the following code:

  let planet = Planet(rawValue: 4)!
  print(planet.mass)

You may argue this is hard to read the more fields the tuple holds,
but there is nothing preventing you from formating your code in
another way, for example:

  enum Planet : (mass: Double, radius: Double, rawValue: Int) {
     case Mercury = (
         mass: 3.303e+23,
         radius: 2.4397e6,
         rawValue: 1
     )
     case Venus = (
         mass: 4.869e+24,
         radius: 6.0518e6,
         rawValue: 2
     )
     // ....

That was my proposal for the problem you presented.

···

On 31 May 2016 at 11:23, Leonardo Pessoa <me@lmpessoa.com> wrote:

As I said before, I'm not in favour of this approach. And you completely
missed my proposal in the alternatives.

________________________________
From: Jānis Kiršteins
Sent: ‎31/‎05/‎2016 11:17 AM
To: Leonardo Pessoa
Cc: Brent Royal-Gordon; swift-evolution
Subject: Re: [swift-evolution] [Proposal] Enums with static stored
propertiesfor each case

I wrote a proposal draft:

# Enum case stored properties

* Proposal: TBD
* Author: [Janis Kirsteins](https://github.com/kirsteins\)
* Status: TBD
* Review manager: TBD

## Introduction

This proposal allows each enum case to have stored properties.

## Motivation

Enums cases can have a lot of constant (or variable) static values
associated with it. For example, planets can have mass, radius, age,
closest star etc. Currently there is no way to set or get those values
easily.

Example below shows that is hard to read and manage static associated
values with each case. It is hard to add or remove case as it would
require to add or remove code in four different places in file. Also
static associated value like `UIBezierPath` is recreated each time the
property is computed while it's constant.

enum Suit {
    case spades
    case hearts
    case diamonds
    case clubs

    var simpleDescription: String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }

    var color: UIColor {
        switch self {
        case .spades:
            return .blackColor()
        case .hearts:
            return .redColor()
        case .diamonds:
            return .redColor()
        case .clubs:
            return .blackColor()
        }
    }

    var symbol: String {
        switch self {
        case .spades:
            return "♠"
        case .hearts:
            return "♥"
        case .diamonds:
            return "♦"
        case .clubs:
            return "♣"
        }
    }

    var bezierPath: UIBezierPath {
        switch self {
        case .spades:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .hearts:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .diamonds:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .clubs:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        }
    }
}

## Proposed solution

Support stored properties for enum cases just as each case were an
instance. Case properties are initialized block after each case
declaration.

enum Suit {
    let simpleDescription: String
    let color: UIColor
    let symbol: String
    let bezierPath: UIBezierPath

    case spades {
        simpleDescription = "spades"
        color = .blackColor()
        symbol = "♠"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case hearts {
        simpleDescription = "hearts"
        color = .redColor()
        symbol = "♥"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case diamonds {
        simpleDescription = "diamonds"
        color = .redColor()
        symbol = "♦"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case clubs {
        simpleDescription = "clubs"
        color = .blackColor()
        symbol = "♣"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }
}

let symbol = Suit.spades.symbol // "♠"

The proposed solution improves:
- Readability as cases are closer with their related data;
- Improves code maintainability as a case can be removed or added in one
place;
- Improved performance as there is no need to recreate static values;
- ~30% less lines of code in given example.

## Detailed design

#### Stored properties

Enum stored properties are supported the same way they are supported
for structs can classes. Unlike enum associated values, stored
properties are static to case and are shared for the same case.

Properties are accessed:

let simpleDescription = Suit.spades.simpleDescription

Mutable properties can be set:

Suit.spades.simpleDescription = "new simple description"

#### Initialization

If enum has uninitialized stored property it must be initialized in a
block after each case declaration. The block work the same way as
struct initialization. At the end of initialization block all
properties must be initialized.

enum Suit {
    var simpleDescription: String

    case spades {
        simpleDescription = "spades"
    }
}

Initialization block can be combine with use of `rawValue`:

enum Suit: Int {
    var simpleDescription: String

    case spades = 1 {
        simpleDescription = "spades"
    }
}

or associated values of the case:

enum Suit {
    var simpleDescription: String

    case spades(Int) {
        simpleDescription = "spades"
    }
}

## Impact on existing code

Stored properties for enums are not currently not supported, so there
is no impact on existing code.

## Alternatives considered

- Use labeled tuple as `rawValue` of the enum case. This approach is
not compatible as it conflicts with intention of `rawValue` of Swift
enum;
- Use per case initializer like [Java
Enum](JDK 21 Documentation - Home).
Swift enum uses custom initializer syntax to setup instances, not
cases. So this approach is not suitable for Swift.

On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think that's the case with enums. You're changing their current
behaviour of only having stored values to one in which it's computed (even
if only once and then stored). Enums are IMO something that have a static
value you know beforehand and can count on. That's why I'm not fond of the
accessor proposal. Otherwise I think we're transforming enums into a closed
set of struct instances and one could do that already by using a private
init.

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution >>> <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >>> <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with
it.

`rawValue` has a different purpose from how you're using it. It's
supposed to allow you to convert your type to some other *equivalent* type,
like an equivalent integer or string. Moreover, it's supposed to allow you
to *reconstruct* the instance from the raw value—remember,
`RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side.
You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that
means you *can't* use it on the same type for its intended purpose. For
instance, you would not be able to assign numbers to your Planet enum's
cases to help you serialize them or bridge them to Objective-C. That's not
good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types
which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's
simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic
requirement for its `allValues` property), you could write a Dictionary-like
type which ensured at initialization time that it was exhaustive. That's not
as good as compile time, but it's not bad—sort of a three-quarters solution.

       struct ExhaustiveDictionary<Key: Hashable, Value where Key:
>: Collection, DictionaryLiteralConvertible {
               private var dictionary: [Key: Value]

               init(dictionaryLiteral elements: (Key, Value)...) {
                       dictionary = [:]
                       for (k, v) in elements {
                               dictionary[k] = v
                       }

                       if dictionary.count != Key.allValues.count {
                               let missingKeys = Key.allValues.filter {
dictionary[$0] == nil }
                               preconditionFailure("ExhaustiveDictionary
is missing elements from \(Key.self): \(missingKeys)")
                       }
               }

               var startIndex: Dictionary.Index {
                       return dictionary.startIndex
               }
               var endIndex: Dictionary.Index {
                       return dictionary.endIndex
               }
               subscript(index: Dictionary.Index) -> (Key, Value) {
                       return dictionary[index]
               }
               func index(after i: Dictionary.Index) -> Dictionary.Index
{
                       return dictionary.index(after: i)
               }

               subscript(key: Key) -> Value {
                       get { return dictionary[key]! }
                       set { dictionary[key] = newValue }
               }
       }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition
site, where you only have to specify the name of the accessor you're
defining, not a `func` or `var` keyword, a return type, or even parameter
names. (Like `willSet`, there's a default parameter name you can use.)
Secondarily, though, I think it's helpful to indicate very explicitly that
this is not an ordinary method or property definition, even if the compiler
could perhaps sort things out without it. `accessor` is something a user can
Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets
example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

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

   case spades {

<snip>

       let bezierPath = UIBezierPath()

Does each instance of `.spades` have a *separate* UIBezierPath, or do all instances of `.spades` share one? If it's the former, I have strong doubts you'll actually get this through. If it's the latter, that isn't really what this syntax suggests is happening.

···

--
Brent Royal-Gordon
Architechies

A huge +1 on the syntax change, which I think is a colossal improvement over the current situation and adds a lot of clarity to enum declarations.

Neutral on the necessity to add actual stored properties to the enums. If the new syntax were merely syntactic sugar that would effectively generate the switch statements behind the scenes, that would work for me too, and would probably offend fewer people.

Charles

···

On May 31, 2016, at 9:17 AM, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I wrote a proposal draft:

# Enum case stored properties

* Proposal: TBD
* Author: [Janis Kirsteins](https://github.com/kirsteins\)
* Status: TBD
* Review manager: TBD

## Introduction

This proposal allows each enum case to have stored properties.

## Motivation

Enums cases can have a lot of constant (or variable) static values
associated with it. For example, planets can have mass, radius, age,
closest star etc. Currently there is no way to set or get those values
easily.

Example below shows that is hard to read and manage static associated
values with each case. It is hard to add or remove case as it would
require to add or remove code in four different places in file. Also
static associated value like `UIBezierPath` is recreated each time the
property is computed while it's constant.

enum Suit {
   case spades
   case hearts
   case diamonds
   case clubs

   var simpleDescription: String {
       switch self {
       case .spades:
           return "spades"
       case .hearts:
           return "hearts"
       case .diamonds:
           return "diamonds"
       case .clubs:
           return "clubs"
       }
   }

   var color: UIColor {
       switch self {
       case .spades:
           return .blackColor()
       case .hearts:
           return .redColor()
       case .diamonds:
           return .redColor()
       case .clubs:
           return .blackColor()
       }
   }

   var symbol: String {
       switch self {
       case .spades:
           return "♠"
       case .hearts:
           return "♥"
       case .diamonds:
           return "♦"
       case .clubs:
           return "♣"
       }
   }

   var bezierPath: UIBezierPath {
       switch self {
       case .spades:
           let path = UIBezierPath()
           // omitted lines ...
           return path
       case .hearts:
           let path = UIBezierPath()
           // omitted lines ...
           return path
       case .diamonds:
           let path = UIBezierPath()
           // omitted lines ...
           return path
       case .clubs:
           let path = UIBezierPath()
           // omitted lines ...
           return path
       }
   }
}

## Proposed solution

Support stored properties for enum cases just as each case were an
instance. Case properties are initialized block after each case
declaration.

enum Suit {
   let simpleDescription: String
   let color: UIColor
   let symbol: String
   let bezierPath: UIBezierPath

   case spades {
       simpleDescription = "spades"
       color = .blackColor()
       symbol = "♠"
       let bezierPath = UIBezierPath()
       // omitted lines ...
       self.bezierPath = bezierPath
   }

   case hearts {
       simpleDescription = "hearts"
       color = .redColor()
       symbol = "♥"
       let bezierPath = UIBezierPath()
       // omitted lines ...
       self.bezierPath = bezierPath
   }

   case diamonds {
       simpleDescription = "diamonds"
       color = .redColor()
       symbol = "♦"
       let bezierPath = UIBezierPath()
       // omitted lines ...
       self.bezierPath = bezierPath
   }

   case clubs {
       simpleDescription = "clubs"
       color = .blackColor()
       symbol = "♣"
       let bezierPath = UIBezierPath()
       // omitted lines ...
       self.bezierPath = bezierPath
   }
}

let symbol = Suit.spades.symbol // "♠"

The proposed solution improves:
- Readability as cases are closer with their related data;
- Improves code maintainability as a case can be removed or added in one place;
- Improved performance as there is no need to recreate static values;
- ~30% less lines of code in given example.

## Detailed design

#### Stored properties

Enum stored properties are supported the same way they are supported
for structs can classes. Unlike enum associated values, stored
properties are static to case and are shared for the same case.

Properties are accessed:

let simpleDescription = Suit.spades.simpleDescription

Mutable properties can be set:

Suit.spades.simpleDescription = "new simple description"

#### Initialization

If enum has uninitialized stored property it must be initialized in a
block after each case declaration. The block work the same way as
struct initialization. At the end of initialization block all
properties must be initialized.

enum Suit {
   var simpleDescription: String

   case spades {
       simpleDescription = "spades"
   }
}

Initialization block can be combine with use of `rawValue`:

enum Suit: Int {
   var simpleDescription: String

   case spades = 1 {
       simpleDescription = "spades"
   }
}

or associated values of the case:

enum Suit {
   var simpleDescription: String

   case spades(Int) {
       simpleDescription = "spades"
   }
}

## Impact on existing code

Stored properties for enums are not currently not supported, so there
is no impact on existing code.

## Alternatives considered

- Use labeled tuple as `rawValue` of the enum case. This approach is
not compatible as it conflicts with intention of `rawValue` of Swift
enum;
- Use per case initializer like [Java
Enum](JDK 21 Documentation - Home).
Swift enum uses custom initializer syntax to setup instances, not
cases. So this approach is not suitable for Swift.

On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think that's the case with enums. You're changing their current behaviour of only having stored values to one in which it's computed (even if only once and then stored). Enums are IMO something that have a static value you know beforehand and can count on. That's why I'm not fond of the accessor proposal. Otherwise I think we're transforming enums into a closed set of struct instances and one could do that already by using a private init.

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >>> <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

      struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
              private var dictionary: [Key: Value]

              init(dictionaryLiteral elements: (Key, Value)...) {
                      dictionary = [:]
                      for (k, v) in elements {
                              dictionary[k] = v
                      }

                      if dictionary.count != Key.allValues.count {
                              let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                              preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                      }
              }

              var startIndex: Dictionary.Index {
                      return dictionary.startIndex
              }
              var endIndex: Dictionary.Index {
                      return dictionary.endIndex
              }
              subscript(index: Dictionary.Index) -> (Key, Value) {
                      return dictionary[index]
              }
              func index(after i: Dictionary.Index) -> Dictionary.Index {
                      return dictionary.index(after: i)
              }

              subscript(key: Key) -> Value {
                      get { return dictionary[key]! }
                      set { dictionary[key] = newValue }
              }
      }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

I'm not sure about my opinion on this proposal, but I believe you should add this as alternatives of how we can have the similar features today without injecting stored properties into enums :

enum Suit {
     case spades
     case hearts
     case diamonds
     case clubs

     struct SuitInfo {
         let simpleDescription: String
         let color: UIColor
         let symbol: String
         let bezierPath: UIBezierPath
     }

     var info : SuitInfo {
         switch self {
         case .spades:
             let path = UIBezierPath()
             // omitted lines ...

             return SuitInfo(
                 simpleDescription: "spades",
                 color: .blackColor(),
                 symbol: ":spades:",
                 bezierPath: path)

         case .hearts:
             let path = UIBezierPath()
             // omitted lines ...

             return SuitInfo(
                 simpleDescription: "hearts",
                 color: .redColor(),
                 symbol: ":heart:",
                 bezierPath: path)

         case .diamonds:
             let path = UIBezierPath()
             // omitted lines ...

             return SuitInfo(
                 simpleDescription: "diamonds",
                 color: .redColor(),
                 symbol: ":diamonds:",
                 bezierPath: path)

         case .clubs:
             let path = UIBezierPath()
             // omitted lines ...

             return SuitInfo(
                 simpleDescription: "clubs",
                 color: .blackColor(),
                 symbol: ":clubs:",
                 bezierPath: path)

         }
     }
}

and this:

enum Suit {
     case spades
     case hearts
     case diamonds
     case clubs

     struct SuitInfo {
         let simpleDescription: String
         let color: UIColor
         let symbol: String
         let bezierPath: UIBezierPath
     }

     static let spadesInfo : SuitInfo = {
         let path = UIBezierPath()
         // omitted lines ...

         return SuitInfo(
             simpleDescription: "spades",
             color: .blackColor(),
             symbol: ":spades:",
             bezierPath: path)
     }()

     static let heartsInfo : SuitInfo = {
         let path = UIBezierPath()
         // omitted lines ...

         return SuitInfo(
             simpleDescription: "hearts",
             color: .redColor(),
             symbol: ":heart:",
             bezierPath: path)
     }()

     static let diamondsInfo : SuitInfo = {
         let path = UIBezierPath()
         // omitted lines ...

         return SuitInfo(
             simpleDescription: "diamonds",
             color: .redColor(),
             symbol: ":diamonds:",
             bezierPath: path)
     }()

     static let clubsInfo : SuitInfo = {
         let path = UIBezierPath()
         // omitted lines ...

         return SuitInfo(
             simpleDescription: "clubs",
             color: .blackColor(),
             symbol: ":clubs:",
             bezierPath: path)
     }()

     var info : SuitInfo {
         switch self {
             case .spades: return Suit.spadesInfo
             case .hearts: return Suit.heartsInfo
             case .diamonds: return Suit.diamondsInfo
             case .clubs: return Suit.clubsInfo
         }
     }
}

···

On 31.05.2016 17:17, Jānis Kiršteins via swift-evolution wrote:

I wrote a proposal draft:

# Enum case stored properties

* Proposal: TBD
* Author: [Janis Kirsteins](https://github.com/kirsteins\)
* Status: TBD
* Review manager: TBD

## Introduction

This proposal allows each enum case to have stored properties.

## Motivation

Enums cases can have a lot of constant (or variable) static values
associated with it. For example, planets can have mass, radius, age,
closest star etc. Currently there is no way to set or get those values
easily.

Example below shows that is hard to read and manage static associated
values with each case. It is hard to add or remove case as it would
require to add or remove code in four different places in file. Also
static associated value like `UIBezierPath` is recreated each time the
property is computed while it's constant.

enum Suit {
    case spades
    case hearts
    case diamonds
    case clubs

    var simpleDescription: String {
        switch self {
        case .spades:
            return "spades"
        case .hearts:
            return "hearts"
        case .diamonds:
            return "diamonds"
        case .clubs:
            return "clubs"
        }
    }

    var color: UIColor {
        switch self {
        case .spades:
            return .blackColor()
        case .hearts:
            return .redColor()
        case .diamonds:
            return .redColor()
        case .clubs:
            return .blackColor()
        }
    }

    var symbol: String {
        switch self {
        case .spades:
            return "♠"
        case .hearts:
            return "♥"
        case .diamonds:
            return "♦"
        case .clubs:
            return "♣"
        }
    }

    var bezierPath: UIBezierPath {
        switch self {
        case .spades:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .hearts:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .diamonds:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        case .clubs:
            let path = UIBezierPath()
            // omitted lines ...
            return path
        }
    }
}

## Proposed solution

Support stored properties for enum cases just as each case were an
instance. Case properties are initialized block after each case
declaration.

enum Suit {
    let simpleDescription: String
    let color: UIColor
    let symbol: String
    let bezierPath: UIBezierPath

    case spades {
        simpleDescription = "spades"
        color = .blackColor()
        symbol = "♠"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case hearts {
        simpleDescription = "hearts"
        color = .redColor()
        symbol = "♥"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case diamonds {
        simpleDescription = "diamonds"
        color = .redColor()
        symbol = "♦"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }

    case clubs {
        simpleDescription = "clubs"
        color = .blackColor()
        symbol = "♣"
        let bezierPath = UIBezierPath()
        // omitted lines ...
        self.bezierPath = bezierPath
    }
}

let symbol = Suit.spades.symbol // "♠"

The proposed solution improves:
- Readability as cases are closer with their related data;
- Improves code maintainability as a case can be removed or added in one place;
- Improved performance as there is no need to recreate static values;
- ~30% less lines of code in given example.

## Detailed design

#### Stored properties

Enum stored properties are supported the same way they are supported
for structs can classes. Unlike enum associated values, stored
properties are static to case and are shared for the same case.

Properties are accessed:

let simpleDescription = Suit.spades.simpleDescription

Mutable properties can be set:

Suit.spades.simpleDescription = "new simple description"

#### Initialization

If enum has uninitialized stored property it must be initialized in a
block after each case declaration. The block work the same way as
struct initialization. At the end of initialization block all
properties must be initialized.

enum Suit {
    var simpleDescription: String

    case spades {
        simpleDescription = "spades"
    }
}

Initialization block can be combine with use of `rawValue`:

enum Suit: Int {
    var simpleDescription: String

    case spades = 1 {
        simpleDescription = "spades"
    }
}

or associated values of the case:

enum Suit {
    var simpleDescription: String

    case spades(Int) {
        simpleDescription = "spades"
    }
}

## Impact on existing code

Stored properties for enums are not currently not supported, so there
is no impact on existing code.

## Alternatives considered

- Use labeled tuple as `rawValue` of the enum case. This approach is
not compatible as it conflicts with intention of `rawValue` of Swift
enum;
- Use per case initializer like [Java
Enum](JDK 21 Documentation - Home).
Swift enum uses custom initializer syntax to setup instances, not
cases. So this approach is not suitable for Swift.

On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think that's the case with enums. You're changing their current behaviour of only having stored values to one in which it's computed (even if only once and then stored). Enums are IMO something that have a static value you know beforehand and can count on. That's why I'm not fond of the accessor proposal. Otherwise I think we're transforming enums into a closed set of struct instances and one could do that already by using a private init.

On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution <swift-evolution@swift.org> wrote:

I agree with the argument about use of "where", not replacing the raw
value and having some kind of initialization block. But I cannot see
why "accessors" concept is any better than stored properties to solve
the particular problem. The "accessors" concept has much wider scope
than enums and is a separate proposal.

On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >>> <brent@architechies.com> wrote:

- Abusing rawValue is just that: an abuse.

My original proposal does not replace rawValue and is compatible with it.

`rawValue` has a different purpose from how you're using it. It's supposed to allow you to convert your type to some other *equivalent* type, like an equivalent integer or string. Moreover, it's supposed to allow you to *reconstruct* the instance from the raw value—remember, `RawRepresentable` has an `init(rawValue:)` requirement.

It is *not* supposed to be an ancillary bag of information on the side. You're cramming a square peg into a round hole here.

(Also, if you use `rawValue` for an ancillary bag of information, that means you *can't* use it on the same type for its intended purpose. For instance, you would not be able to assign numbers to your Planet enum's cases to help you serialize them or bridge them to Objective-C. That's not good.)

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

It is also used in generic type constraints. Plus it reads like human
language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)`

But a generic constraint is also a type of condition: it specifies types which are permitted and divides them from types that are not.

This is *not* a condition. It's not anything like a condition. It's simply not consistent with anything else in the language.

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

Dictionaries can be used as workaround, but they cannot (lack of
exhaustiveness) solve the problem.

I agree that they're a halfway solution.

If `ValuesEnumerable` were to be accepted (and to have a generic requirement for its `allValues` property), you could write a Dictionary-like type which ensured at initialization time that it was exhaustive. That's not as good as compile time, but it's not bad—sort of a three-quarters solution.

       struct ExhaustiveDictionary<Key: Hashable, Value where Key: ValuesEnumerable>: Collection, DictionaryLiteralConvertible {
               private var dictionary: [Key: Value]

               init(dictionaryLiteral elements: (Key, Value)...) {
                       dictionary = [:]
                       for (k, v) in elements {
                               dictionary[k] = v
                       }

                       if dictionary.count != Key.allValues.count {
                               let missingKeys = Key.allValues.filter { dictionary[$0] == nil }
                               preconditionFailure("ExhaustiveDictionary is missing elements from \(Key.self): \(missingKeys)")
                       }
               }

               var startIndex: Dictionary.Index {
                       return dictionary.startIndex
               }
               var endIndex: Dictionary.Index {
                       return dictionary.endIndex
               }
               subscript(index: Dictionary.Index) -> (Key, Value) {
                       return dictionary[index]
               }
               func index(after i: Dictionary.Index) -> Dictionary.Index {
                       return dictionary.index(after: i)
               }

               subscript(key: Key) -> Value {
                       get { return dictionary[key]! }
                       set { dictionary[key] = newValue }
               }
       }

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.

Wouldn't accessor just be a redundant keyword here? Currently enums do
not support stored properties, so I guess there is no extra need to
mark properties with any special keyword.

The keyword is mainly to indicate the unusual syntax at the definition site, where you only have to specify the name of the accessor you're defining, not a `func` or `var` keyword, a return type, or even parameter names. (Like `willSet`, there's a default parameter name you can use.) Secondarily, though, I think it's helpful to indicate very explicitly that this is not an ordinary method or property definition, even if the compiler could perhaps sort things out without it. `accessor` is something a user can Google if they've never seen it before.

Property accessors might work for enums with associated values, but
not so well without them.

The two have nothing to do with each other. I showed your planets example, which has no associated values but uses accessors just fine.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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