[swift-evolution] ValueEnumerable protocol with derived implementation for enums

Jacob Bandes-Storch jtbandes at gmail.com
Sat Apr 23 00:18:42 CDT 2016


On Sat, Apr 16, 2016 at 5:20 AM, plx via swift-evolution <
swift-evolution at swift.org> wrote:

>
>
> 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.
>

Thank you for bringing this up; I hadn't thought about it. Indeed, the
library evolution design document <
http://jrose-apple.github.io/swift-library-evolution/#enums> states that
adding new cases, adding raw types, and reordering cases should be
binary-compatible changes.

I hope someone who knows more about the resilience design can weigh in
here. I'll CC Jordan Rose and John McCall, authors of that document, on
this email.

I think you're right that the implications of requiring an array might be
significant, if this array is exported as public API in a module which
other binaries depend on. So I wonder if it might be possible to pursue a
solution which *doesn't* export any additional public API in a module.

Recall that we'd like to be able to add ValuesEnumerable support in an
extension, both on Swift enums and on enums imported from Obj-C. Seems like
you might *not* want those conformances to be exported, so that future
changes in the type of allValues wouldn't have to break existing compiled
binaries. (But currently, IIUC, extensions which add protocol conformances
must be public.)

I'm almost wondering whether we should be doing something like
*#allValues(MyEnum)*, which uses # to indicate "compiler magic" (for now it
would produce an Array<MyEnum>), gathering the available cases from the
module at compile time. At some time in the future, when reflection is much
more mature, perhaps this could be replaced with a standard library
function.

---

This also prompted me to research Java's implementation a bit more. I'm not
a Java user, let alone expert, but here's what I found:

Class.getEnumConstants() returns the values *in source order*.  <
https://docs.oracle.com/javase/tutorial/reflect/special/enumMembers.html>
The page also says the following:

*Note: For various reasons, including support for evolution of the enum
type, the declaration order of enum constants is important.
Class.getFields() and Class.getDeclaredFields() do not make any guarantee
that the order of the returned values matches the order in the declaring
source code. If ordering is required by an application, use
Class.getEnumConstants().*

There's also a section on "Evolution of Enums" in this page about Binary
Compatibility: <
https://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.26>
 *"Adding
or reordering constants in an enum type will not break compatibility with
pre-existing binaries."*

Point being that getEnumConstants() always returns an array, it just might
have different things in it depending on the version of the class you're
interrogating.


# 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).
>

CaseEnumerable was just an earlier name for Value(s)Enumerable. The stuff
in the "Future directions" section remains speculative. I think we should
keep the proposal focused if we want it to ever happen; improvements can
come later.


Jacob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160422/3bce9fb7/attachment.html>


More information about the swift-evolution mailing list