[swift-evolution] [Proposal] Enum subsets

Tony Allevato allevato at google.com
Fri Jun 3 17:02:27 CDT 2016


I like the idea, conceptually—having more set-like semantics for enums
sounds like it would open up some interesting possibilities. (And I don't
think we should limit ourselves to subsets either. Supersets are also
interesting.)

Things could get pretty tricky though, so there are a lot of gaps to fill
in here. Off the top of my head, I'd be curious about the following
situations:

* You don't mention cases with associated values anywhere. Is it your
intention to prohibit those, or would the following work?

enum Foo {
   case Bar
   case Baz(Int)
   case Blorp(String)
}
typealias FooSubset = Foo.(Bar|Baz)
// FooSubset is effectively: enum FooSubset { case Bar; case Baz(Int) }?

I can't think of any reason off the top of my head to prohibit cases with
associated values, for what it's worth.

* How would non-case members of an enum (i.e., computed properties,
methods, etc.) be effected? Other folks in the thread have mentioned
methods that switch on self. In a very abstract sense, there are two kinds
of these properties/methods: those that simply use "self" to branch, and
those that use the enum in other ways. For example, I see no reason that
this shouldn't work:

enum Color {
  case red
  case blue
  case green

  var uiColor: UIColor {
    switch self {
      case .red: return UIColor.red()
      case .blue: return UIColor.blue()
      case .green: return UIColor.green()
    }
  }
}
let x: Color.(blue|green) = Color.blue
let color = x.uiColor

But on the other hand, you could have something like this:

enum Color {
  case red
  case blue
  case green

  var nextColor: Color {
    switch self {
      case .red: return .blue
      case .blue: return .green
      case .green: return .red
    }
  }
}
let x: Color.(blue|green) = Color.blue
let color = x.nextColor  // umm....

Things start getting really hard to predict because it's very hard to
reason about what happens inside these computed properties, and I fear that
the solution becomes "conservatively exclude a lot of stuff" and that
starts taking a lot of the power away from those enums.



On Fri, Jun 3, 2016 at 2:35 PM T.J. Usiyan via swift-evolution <
swift-evolution at swift.org> wrote:

> Since this seems to have some interest, I've made a gist.
>
> https://gist.github.com/griotspeak/963bc87a0c244c120264b11fb022d78c
> Introduction
>
> This proposal adds/creates syntax to allow ad hoc creation of enums whose
> members are strict subsets of explicitly defined enums.
>
> Swift-evolution thread: Discussion thread topic for that proposal
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160530/020025.html>
> Motivation
>
> Consider a situation where we have an enum Color which represents the
> entire set of colors relevant to your application with many salient methods
> and operations. We have also declared an enum LCDColorModel with only
> three colors, red, blue, green .
>
> enum Color {
>
>
> case red, orange, yellow, green, blue, indigo, violet
>> }
>
>
> enum LCDColor {
>     case red, green, blue
> }
>
> The cases in LCDColor in our scenario do not require different behavior
> from their similarly named cases in Color. We would like, simply stated,
> to explicitly restrict the cases allowed within a specific portion of our
> software. There are, currently, a few approaches
> 1. Duplicate functionality in LCDColor
>
>
> - Completely manually
> - Protocols with ‘minimal’ manual duplication
>
> 2. Avoid duplication by allowing conversion to Color.
>
> Neither of these solutions make the subset relationship between Color and
> LCDColor clear or strict.
> Proposed solution
>
> Add syntax to describe a restricted set of cases from an enum.
>
> typealias LCDColor = Color.(red|green|blue)
>
> LCDColor has all of the type and instance methods of Color.
>
>    - Barring any technical reason, the ‘actual’ name of the type, in this
>    example, is Color.(red|green|blue) This makes the relationship to Colorsyntactically
>    clear. If a typealias is not desired, Color.(red|green|blue) should
>    refer to the same type as LCDColor
>    - Switching over Color.(red|green|blue) should only need to be
>    exhaustive for the three cases .red, .green, and .blue.
>    - Two initializers should be implicitly created
>       - Color to LCDColor?
>          - returns nil for all cases not in LCDColor
>       - LCDColor to Color
>          - Obvious and trivial implementation mapping cases from LCDColor
>           to Color
>       - Casting should be allowed
>       - from superset to subset only using as? or as! syntax.
>       - from subset to superset using as
>    - Creating subsets of subsets is not allowed but reasonable
>    conversions among subsets should be allowed if technically feasible such
>    that:
>       - Given subsets of C A and B, where A is a superset of B, the
>       casting relationship between A and B should be similar to that
>       between C and either of the other two named subsets.
>
> Detailed design
>
> While I am unsure of the entirety of the design, I propose that name
> mangling be used which, along with the declaration order restriction should
> mean that all possible subsets have a stable and predictable name which
> contains all of the information necessary to infer cases.
>
> If a mangled name approach is taken, the ordering of cases should be
> sorted to ensure stability.
> Alternatives considered
>
>    - Do nothing. This feature is not strictly necessary but does allow
>    for expressivity not currently available in the language.
>    - implicitly create properties which convert to superset type.
>
> Impact on existing code
>
> This is an additive change which should have no breaking change to
> existing code.
>
> On Fri, Jun 3, 2016 at 4:57 PM, Austin Zheng <austinzheng at gmail.com>
> wrote:
>
>> I really like the idea behind this proposal.
>>
>> Some questions:
>>
>> - Would the enum 'slice' be a distinct type relative to the base enum?
>> - On a related note, would shared cases between the sliced enum and the
>> base enum be implicitly convertible?
>> - If they aren't implicitly convertible between each other, would there
>> be an affordance to perform conversions (e.g. a "parentEnumType" property
>> and an "init?(parentEnumType:)" initializer)/
>> - Would you be able to further slice a sliced enum? If so, would they
>> share the same parent, or would the 'parent' of the sliced sliced enum be
>> the sliced enum?
>> - If the parent enum has members that switch on 'self', would those
>> members be available to the child automatically?
>> - What happens if you have two (or more) slices with disjoint members?
>> (e.g. 'LCDColors' and 'TrafficLightColors') Would they be considered
>> completely separate 'sub-types'?
>>
>> Best,
>> Austin
>>
>>
>>
>>
>> On Fri, Jun 3, 2016 at 6:22 AM, T.J. Usiyan via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> This is loosely related to but not meant to 'compete' with the ad hoc
>>> enum proposal.
>>>
>>> ## Introduction
>>>
>>> This proposal adds/creates syntax to allow ad hoc creation of enums
>>> whose members are strict subsets of explicitly defined enums.
>>>
>>> Swift-evolution thread: [Discussion thread topic for that proposal](
>>> http://news.gmane.org/gmane.comp.lang.swift.evolution)
>>>
>>> ## Motivation
>>> Consider a situation where we have an enum `Colors` which represents the
>>> entire set of colors relevant to your application with many salient methods
>>> and operations. We have also declared an enum `LCDColorModel` with only
>>> three colors, `red, blue, green` .
>>>
>>> ``` swift
>>> enum Colors {
>>> case red, orange, yellow, green, blue, indigo, violet
>>>>>> }
>>>
>>> enum LCDColors {
>>> case red, green, blue
>>> }
>>> ```
>>>
>>> The cases in `LCDColors` in our scenario do not require different
>>> behavior from their similarly named cases in `Colors`. We would like,
>>> simply stated, to explicitly restrict the cases allowed within a specific
>>> portion of our software. There are, currently, a few approaches
>>> 1. Duplicate functionality in `LCDColors`
>>> - Completely manually
>>> - Protocols with 'minimal' manual duplication
>>> 2. Avoid duplication by allowing conversion to `Colors`.
>>>
>>> Neither of these solutions make the subset relationship between `Colors`
>>> and `LCDColors`  clear or strict.
>>>
>>> ## Proposed solution
>>>
>>> Add syntax to describe a restricted set of cases from an enum.
>>>
>>> ```swift
>>> typealias LCDColors = Colors.(red|green|blue)
>>> ```
>>>
>>> `LCDColors ` has all of the type and instance methods of `Colors`. Cases
>>> must appear in the same order as their original declaration.
>>>
>>>
>>> ## Detailed design
>>>
>>> While I am unsure of the entirety of the design, I propose that name
>>> mangling be used which, along with the declaration order restriction should
>>> mean that all possible subsets have a stable and predictable name which
>>> contains all of the information necessary to infer cases.
>>>
>>> ## Impact on existing code
>>>
>>> This is an additive change which should have no breaking change to
>>> existing code.
>>>
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160603/6e750c7d/attachment.html>


More information about the swift-evolution mailing list