> On Apr 15, 2016, at 9:00 PM, Jacob Bandes-Storch via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> This discussion is about a proposal for API to enumerate/count all possible 
> values of a type, particularly enums. The goal is to address potential issues 
> with an old proposal, so it can go up for review soon.
> 
> Core Team / Standard Library Team feedback would be particularly welcome 
> here, because we want this feature to mesh well with the goals & future 
> directions that are already in mind for Swift. If any details the core team 
> would like to see are missing from the proposal, please let us know now.
> 
> Background reading:
> 
>     • 2015-12-08: "List of all Enum values (for simple enums)" — 
> http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064>
>     • 2015-12-21: "Proposal: Enum 'count' functionality" 
> http://thread.gmane.org/gmane.comp.lang.swift.evolution/644 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/644>
>     • 2016-01-17: "Draft Proposal: count property for enum types" 
> http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678>
>     • 2016-01-18: "Pre-proposal: CaseEnumerable protocol (derived collection 
> of enum cases)" at 
> http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701>
>     • 2016-01-20: My subsequent proposal PR #114: 
> https://github.com/apple/swift-evolution/pull/114 
> <https://github.com/apple/swift-evolution/pull/114>
> 
> A lot has happened since then:
> 
>     • 2016-03-03: "[Manifesto] Completing Generics" 
> http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484>
>     • 2016-03-03: "[Accepted with modifications] SE-0023 API Design 
> Guidelines" http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585 
> <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585> & 
> http://apple.github.io/swift-internals/api-design-guidelines/ 
> <http://apple.github.io/swift-internals/api-design-guidelines/>
>     • 2016-03-09: Brent's proposal PR #199: 
> https://github.com/apple/swift-evolution/pull/199 
> <https://github.com/apple/swift-evolution/pull/199>
> 
> Brent brought up some great points in his proposal, but ultimately closed the 
> PR in anticipation of further discussion. I'm sorry I haven't had much time 
> to put into this until now, but I'd like us to get the discussion going again.
> 
> I believe the community is in agreement about the following:
> 
>     • The "allValues" behavior should be provided by conformance to some 
> protocol, named ValueEnumerable or ValuesEnumerable or similar.
>     • The compiler should derive an allValues implementation for "simple" 
> enums (those without associated values).
> 
> There are a couple things which we must still decide:
> 
> ### Should the ValueEnumerable protocol expose the allValues property, or 
> should it be an empty protocol like ErrorType (ErrorProtocol)? If exposed, 
> what is its type?

# General Remarks

My 2c is that if this is to go in the standard library, it should be done 
“right”, which would be more like this version of it:

protocol ValueEnumerable {
  associatedtype ValueCollection : Collection where 
ValueCollection.Iterator.Element == Self
  static var allValues: ValueCollection
}

…and that this concept should simply *wait* for that feature to be available 
before going into the standard library. 

The reason I say this is simply b/c it sounds like this proposal wants to be 
able to support more than simple enumerations, in which case having some 
flexibility in the representation seems appropriate. Consider e.g.:

  struct AxisPolicy3D<Policy:protocol<Equatable,ValueEnumerable>> {
    var x: Policy
    var y: Policy
    var z: Policy
  }

  extension AxisPolicy3D : ValueEnumerable {

    static let allValues: ValueCollection = 
product(Policy.allValues,Policy.allValues,Policy.allValues).lazy.map() { 
        (x,y,z) 
        in
        AxisPolicy3D(x: x, y: y, z: z)
    }

  }

…and similar, wherein the cost of *requiring* an array here could become rather 
large.

But I have a couple general concerns here:

# Resiliency 

My understanding is that the design for resiliency vis-a-vis enumerations is 
meant to allow enumerations to have cases added in future revisions (and 
perhaps also private cases? I didn’t follow resiliency closely). 

If that’s right, and this protocol is supposed to go into the standard library, 
it might also need to address such issues. I have no help to offer and would 
love to be wrong about this point.

# Scope (or: Beyond Simple Enumerations?)

On the one hand, I don’t see any reason *not* to design the protocol so that it 
*could* be adopted by types other than simple enumerations. 

On the other hand, I think the value of having this in place for 
simple-enumerations is huge, and I’m more than a bit skeptical how often it’ll 
actually make sense for anything other than simple-enumerations—it’s easy to 
come with other types that could support it, I just think they’re somewhat rare 
in practice! 

To me, this leaves me thinking that trying to support non-simple enumerations 
is a nice-to-have, but something I’d be willing to drop if it posed any real 
difficulties.

# Ordering

I have mixed feelings here. For comparable values I think it’d be nice if 
`allValues` was ordered, but I think that gets in the way of synthesis if you 
also support types other than simple-enumerations; I am admittedly skeptical 
about trying to support such types, but there you have it.

If you are willing to go there, you can include something like 

  protocol ComparableValueEnumerable : Comparable, ValueEnumerable {

    associatedtype OrderedValueCollection: Collection where 
OrderedValueCollection.Iterator.Element == Self
    static var allValuesInOrder: OrderedValueCollection { get }
  }

…which would be the same as the basic protocol but guarantees the values are 
enumerated in order.

For compiler-synthesized enumerations for suitable types this could simply 
return `allValues`.

# Synthesis

Related to the above, compiler synthesis is great, but given the lack of 
similar synthesis for other types I wouldn’t want to require—or expect—too much 
here.

EG if limited to simple enumerations I could easily see the compiler’s 
synthesis rule be:

- user supplied `allValues`? go with that!
- otherwise:
  - define `allValues` to be equivalent-to `Range<RawValue>.lazy.map() { 
Self(rawValue: $0)! }` when possible (*)
  - otherwise, define `allValues` as an array

…as something along those lines seems reasonable to implement and to understand 
as a user.

(*) “Possible” is something like `RawRepresentable`, the `RawValue` type is 
`Comparable`, the enumeration cases occupy a contiguous range of `RawValue` 
values, and (trickier!) if comparable, `Comparable` ordering is the same as the 
ordering derived-from the raw values.

# Other Remarks

I see the `CaseEnumerable` discussion in the other discussion. It’s certainly 
related, but it’s something with enough independent utility I wouldn’t want it 
to get “lost” in this topic (especially since I think this topic is a great 
feature for the language, but one that may be awhile coming). 

> 
> If allValues were exposed as part of the protocol, then the generic 
> constraint <T: ValueEnumerable> could be used meaningfully, i.e. you could 
> write/use "T.allValues".
> 
> On the other hand, the limitations of the current generics system don't allow 
> "associatedtype ValueCollection: Collection where 
> ValueCollection.Iterator.Element == Self". Doug's Completing Generics 
> manifesto included "Arbitrary requirements in protocols", under the category 
> of "Minor extensions", which would remove this limitation. If this gets 
> implemented, I think it makes a lot of sense to use it here.
> 
> Until then, though, we'd have to pick a concrete type for the collection. 
> Brent proposed that it be an Array, "static var allValues: [Self]".
> 
> The biggest reason I didn't expose allValues on the protocol was that I 
> figured we'd want to allow for efficient implementations which wouldn't 
> require allocating storage for *all* the values (just the endpoints, for 
> instance), but could still count and iterate over them.
> 
> Another question on the subject of exposing the property as a protocol 
> requirement: What should the diagnostics look like if it's missing? Maybe 
> something like this:
> 
>     struct MyType: ValueEnumerable { }
>     // error: type 'MyType' does not conform to protocol 'ValueEnumerable'
>     // note: protocol requires property 'allValues' with type '[MyType]'
>     // note: implementation of allValues cannot be automatically derived for 
> a non-enum type
> 
> ### Should allValues implementations be derived for Comparable enums? What if 
> the sorted order does/doesn't match the source order?
> 
> Brent has suggested the semantics of allValues should be such that for 
> Comparable types, allValues is guaranteed to be ordered. If that were the 
> case, we might not want to require the compiler to derive a ValueEnumerable 
> implementation, since the source order may not match the Comparable-sorted 
> order, and verifying this could overly complicate things. (I think I'm in 
> agreement here: having the values be ordered is a good implementation of the 
> principle of least surprise.)
> 
> 
> Thoughts welcome.
> 
> Jacob
> _______________________________________________
> 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

Reply via email to