> On Apr 15, 2016, at 9:00 PM, Jacob Bandes-Storch via swift-evolution
> <[email protected]> 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
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution