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

Matthew Johnson matthew at anandabits.com
Mon Feb 27 12:39:59 CST 2017



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)
}


> 
>> The second open question is the syntax for “overloaded” cases. If we decide to allow them, what should the patterns matching them look like? I can think of one obvious-ish design where we make the pattern look like the declaration and require types for disambiguation. So the most verbose form of pattern would look something like
>> 
>> ```
>> case let .baseName(label0 name0: Type0, label1 name1: Type1)
>> ```
> 
> By "overloaded", do you mean "same name different types", or "same base name, different argument names"? I think we should have a consistent naming model where the latter is never considered overloading. As an affordance to make pattern matching more concise, it seems reasonable to me to maybe say that a binding pattern matches a label with the same name, so that `case .foo(let bar, let bas)` can match `.foo(bar:bas:)`.

I agree as far as names go, but I think this was talking about same name different types. I believe the overloading sub-topic began with Dave A's comments about anonymous cases for enums where the associated value is all that matters. 

My suggestion was to allow overloading on types and use cast patterns to disambiguate when matching.  This increases consistency with functions and enables that important use case.


> 
> -Joe
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list