[swift-evolution] [swift-evolution-announce] [Review] SE-0155: Normalize Enum Case Representation

Matthew Johnson matthew at anandabits.com
Mon Feb 27 12:57:14 CST 2017


> On Feb 27, 2017, at 12:46 PM, Joe Groff <jgroff at apple.com> wrote:
> 
> 
>> On Feb 27, 2017, at 10:39 AM, Matthew Johnson <matthew at anandabits.com> wrote:
>> 
>> 
>> 
>> Sent from my iPad
>> 
>>> On Feb 27, 2017, at 12:00 PM, Joe Groff via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> 
>>>> On Feb 24, 2017, at 9:26 PM, Daniel Duan <daniel at duan.org> wrote:
>>>> 
>>>> Before I start revising this proposal, there are a couple of open questions I’d like to discuss with the community and the core team.
>>>> 
>>>> The first question relates to the purpose of having a “internal” argument name. There are applications of such names in GADT (if we ever get there) and perhaps the case-as-subtype-of-the-enum stories on the list right now. Out side of these scenarios, however, such names has few chances to be used. The one I can come up with, which is also the “open” part of the question, is this: we can use the internal names in pattern matching, as opposed to using the labels. This seems to align with the subtyping/GADT use cases. Is this a desirable outcome?
>>> 
>>> Why would GADTs make internal argument names useful? They seem completely useless to me. Their "internal"-ness is compromised if you try to hang semantics off of them—they shouldn't have any impact on use sites.
>> 
>> Internal is probably a bad name.  Imagine if the case is a subtype of the enum and thus a struct or struct-like type.  We would usually want the property to be named similarly to the name we would use for an argument in a function implementation, not the label used when calling the function.
>> 
>> Here is an example of the kind of thing I have in mind:
>> 
>> enum PlayerState {
>>  case stopped
>>  sub case playing(with track: Track)
>> 
>>   // type of the playing case looks something like this:
>>   struct Playing { let track: Track }
>> 
>>   // the case value constructor looks something like this
>>   static func playing(with track: Track) -> Playing {
>>       return Playing(track: track)
>>   }
>> }
>> 
>> let foo = Foo.playing(with: makeTrack())
>> let track = foo.track
>> 
>> switch foo {
>>   case .stopped: break
>>   // we get to match with the name that is argument / property-like
>>   // `with` would be a terrible variable name 
>>   case .playing(let track): // do something with the track
>> 
>>    // this label would be used when the user wants to use a *different* name
>>   // case .playing(with: let someOtherName)
>> }
> 
> That still feels like it's going against the behavior of the binding name in other declarations. Personally, `case .playing(with: let x)` doesn't bother me that much, especially since the IDE ought to be able to splat that out for you.

It depends on how verbose the label is.  :)  

What did you think of the subtype example using the “internal” name as the property name?  Does that make sense?

I definitely don’t think we would want to allow eliding the *external* label by binding a name: `case .playing(let with)` is a bit absurd! :)

Personally, I think eliding would be a useful shorthand and if we allow it along with internal names (which I also think is a good idea) then we would want eliding to use the internal / property name.  

The primary goal should be clarity at the site of the pattern.  Sometimes a label adds clarity, but other times it will just add clutter.  Allowing label elision ensures a reader sees a name that lines up with the meaning of the associated value with less clutter.  I think this would often increase clarity.  That said, it is a rather minor piece of syntactic sugar.


> 
> -Joe



More information about the swift-evolution mailing list