[swift-evolution] [draft] Compound Names For Enum Cases

Christopher Kornher ckornher at me.com
Wed Jan 25 12:48:07 CST 2017


Would using arbitrary label names in statements like `case .foo ( let bar )` still work?  See my example below? There is some code that does this, but it can be fixed with a simple string replacement for nearly all cases, I would think.


> On Jan 24, 2017, at 8:10 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org> wrote:
> 
> No code would be broken. Like functions, case `bar(a:)` can be referred to as `bar` as long as there is no ambiguity. No existing code can have more than one case named bar.
> On Tue, Jan 24, 2017 at 19:17 Christopher Kornher <ckornher at me.com <mailto:ckornher at me.com>> wrote:
> Sorry for not connecting the dots…yet another example of why we need a better way to handle long discussions :)
> …and the dangers of multitasking, perhaps…
> 
> I guess that this shows that having multiple cases with the same name, distinguished by the type of associated value, is possible without this proposal, for whatever that may be worth.
> 
> It is anecdotal, perhaps, but this proposal will break a lot of my code. I tend to use labels as public names for associated values, and different internal names. Forcing consistent naming is sensible. It would come at the cost of breaking a significant amount of existing code and making the language more “fiddly”.
> 
> Comparisons have been made to function signatures, but enums cases with > 2 values are rare in my experience, and functions can often have  > 2 arguments. Perhaps enums with more associated values will become more common if enum cases become more like structure declarations with well defined element names.
> 
> Just a random thought: Treating enum cases like structs would become more reasonable as a result of this proposal i.e. they could conform to protocols. I am not sure that anyone wants to worry about functions for enum cases, but it might be useful to pass enum cases directly to functions that would normally need a struct. Unifying enums and structs in this way might an interesting thing to consider. 
> 
> Associated values that can differ for each enum case seems to be a feature unique to Swift. Is this a feature of any other language?
> 
>> On Jan 24, 2017, at 4:55 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Yes, but *this* proposal is precisely to make the label part of the name.
>> 
>> 
>> On Tue, Jan 24, 2017 at 17:51 Christopher Kornher <ckornher at me.com <mailto:ckornher at me.com>> wrote:
>>> On Jan 24, 2017, at 4:24 PM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> I'm now confused who is arguing for what. Enums cases cannot have the same name. As far as I'm aware, this proposal does not seek to change that. Each case must still be unique. It only changes whether labels are regarded as part of the name or part of the associated type.
>> 
>> Labels are currently neither part of the name nor part of the associated type. See my example (it runs in the playground in Xcode 8.2.1).
>> 
>> Forgive any lack of precise terminology:  Labels for enums with associated values are currently only used to create a "function signature" for constructing values for the enum. They are ignored in all other uses of the enum AFAIK.
>> 
>> My proposal is a way to allow multiple cases with the same name without breaking existing code. It has the benefit of giving enums a richer meaning, in simple English:
>> 
>> Here is an example of a way to handle this currently and what it would become:
>> 
>> // Runs now
>> enum MultipleTypes {
>>     case anInt( value: Int )
>>     case aString( value: String )
>> }
>> 
>> enum E {
>>     case a( a: MultipleTypes )
>> }
>> 
>> let theCurrentWay = E.a( a: .aString( value: "str") )
>> 
>> switch theCurrentWay {
>> case .a( let x ) :
>>     switch x {
>>     case .anInt( let x ) :
>>         break
>>         
>>     case .aString( let x ) :
>>         break
>>     }
>> }
>> 
>> // Proposed:
>> 
>> /*
>> enum theNewWay {
>>      case a( value: Int ) :
>>      case a( value: String ) :
>> }
>> 
>> switch theCurrentWay {
>> case .a( let x: Int ) :
>>     break
>> 
>> case .a( let x: String ) :
>>     break
>> }
>> */
>> 
>> 
>>> 
>>> 
>>> On Tue, Jan 24, 2017 at 17:16 Christopher Kornher via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> On Jan 24, 2017, at 4:02 PM, Daniel Duan <daniel at duan.org <mailto:daniel at duan.org>> wrote:
>>>> 
>>>> 
>>>> 
>>>> Daniel Duan
>>>> Sent from my iPhone
>>>> 
>>>> On Jan 24, 2017, at 2:53 PM, Daniel Duan via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>> 
>>>>> 
>>>>> 
>>>>> Daniel Duan
>>>>> Sent from my iPhone
>>>>> 
>>>>> On Jan 24, 2017, at 2:18 PM, Christopher Kornher via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>> 
>>>>>> I agree that  this rule would be consistent with the handing of Swift function signatures, but my proposal is self-consistent and:
>>>>>> 
>>>>>> 1) It is more powerful, matching on the case name or not as required.
>>>>>> 2) Is consistent with existing switches that ignore associated values
>>>>>> 3) Is constant with ignored labels in other Swift constructs (proposed and in past versions of Swift, at least)
>>>>>> 4) Avoids a possible API change for current enums. This is currently legal but would not be if the enum case “name” is the entire signature for constructing it:
>>>>>> 
>>>>>> ```
>>>>>> enum E {
>>>>>>     case a( a: Int )
>>>>>>     case b( a: Int )
>>>>>> }
>>>>>> 
>>>>>> let anEmum = E.a( a: 4 )
>>>>>> 
>>>>>> 
>>>>>> switch anEmum {
>>>>>> case .a( let x ) :
>>>>>>     break
>>>>>> case .b( let x ) :
>>>>>>     break
>>>>>> }
>>>>>> 
>>>>>> switch anEmum {
>>>>>> case .a( let y ) :
>>>>>>     break
>>>>>>     
>>>>>> case .b( let y ) :
>>>>>>     break
>>>>>> }
>>>>>> ```
>>>>>> So is this proposal worth creating a breaking change? I don’t think so.  it would be possible to ignore the associated parameter name, but not the type, but that would further confuse case matching, I believe.
>>>>>> 
>>>>>> I personally don’t see much purpose in having two enum cases with the same name if they are always treated as distinct and there is no way to reason on the case name.
>>>>> 
>>>>> Would you say the same for functions? Our opinions differ in what constitute a "name". In Swift, function names include parameters. Each enum case declares a function that creates a enum value. That's why it's *more* consistent for enum to have a similar definition of "name".
>>>>> 
>>>>> As for pattern matching, number of patterns the number of cases being matched have a many-to-one relationships. This is true in Swift and many 
>>>>> 
>>>> 
>>>> (Ah, accidentally sent it on my iPhone. My apologies.)
>>>> 
>>>> … other languages. What you are proposing complicates the rules by making this untrue. We'd have to special-case this feature in our pattern matching algorithm.
>>> 
>>> I am not proposing a breaking change. I believe that it is the best way to handle “overloading” enum names without breaking existing code. I don’t believe that this proposal changes any existing logic or rules.
>>> 
>>> The use of the terms "name” and "signature" is well established in many languages. e.g.: https://en.wikipedia.org/wiki/Type_signature <https://en.wikipedia.org/wiki/Type_signature> I am trying to use the terms in this context.
>>> 
>>> Sharing a function name (with different signatures)  is an aid to understanding the purpose of functions and is part of many languages. I do not believe that extending the concept to enums is worth the cost, especially since it will break existing Swift code.
>>> 
>>> You are proposing a source breaking change and I do not agree that is is an improvement. What is the benefit of this change?
>>> 
>>> 
>>> 
>>>> 
>>>>>> If they are always distinct, then requiring different names eliminates unnecessary cognitive load. There is a good reason for functions to have the same “name” and different signatures, and there are precedents in many languages. 
>>>>>> 
>>>>>> You may disagree, but I believe that enums exist to support reasoning on distinct cases and that muddying that by treating the associated value as part of the “signature" increases the language “surface area” while adding no tangible benefit.
>>>>>> 
>>>>>> 
>>>>>>> On Jan 24, 2017, at 2:23 PM, Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>> 
>>>>>>>> 
>>>>>>>> On Jan 24, 2017, at 3:10 PM, Christopher Kornher via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>> 
>>>>>>>> Your example is only has only one case, which is not typical. Perhaps I am missing something, but the only reason that I can imagine for having a case with multiple ways to “construct” it is to have all variants of the case to match. If you don’t want them to match, use a different case name. 
>>>>>>> 
>>>>>>> It sounds like you are missing something.  The `bar(a:)` and `bar(b:)` are the full case names.  These are *not* the same case.  The `bar` shorthand is allowed when there is no ambiguity, however for an enum with both `bar(a:)` and `bar(b:)` there *is* ambiguity and therefore the `bar` shorthand is not allowed.  The programmer is required to spell out the full name of the case they wish to match.
>>>>>>> 
>>>>>>> 
>>>>>>>> 
>>>>>>>> It would still be possible to match on the different types of bar when needed:
>>>>>>>> 
>>>>>>>> ```
>>>>>>>> enum Foo {
>>>>>>>>     case bar(a: Int)
>>>>>>>>     case bar(b: String)
>>>>>>>>     case notAbar
>>>>>>>> }
>>>>>>>> 
>>>>>>>> 
>>>>>>>>  switch aFoo {
>>>>>>>>     case .bar( let a: Int) : // matches Ints only 
>>>>>>>> 	...
>>>>>>>> 
>>>>>>>>     case .bar( let b: String) : // matches Strings only
>>>>>>>>        ...
>>>>>>>> }
>>>>>>>> 
>>>>>>>> switch aFoo {
>>>>>>>>     case .bar :  // Matches both  cases and that is a good thing
>>>>>>>>>>>>>>>> 
>>>>>>>>     case notAbar:
>>>>>>>> 	….
>>>>>>>> }
>>>>>>>> 
>>>>>>>> ```
>>>>>>>> 
>>>>>>>>> On Jan 24, 2017, at 5:27 AM, Xiaodi Wu via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>>> 
>>>>>>>>> I would imagine it would be logical to have it work just like it does now with functions. If case bar is distinct, then that should still work, but if bar is "overloaded," then case bar should be invalid for ambiguity. Seems fine to me, shouldn't break any existing code and therefore we don't lose anything.
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>> On Tue, Jan 24, 2017 at 01:13 David Hart via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>> On 24 Jan 2017, at 00:52, Joe Groff via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>>>> 
>>>>>>>>>> We're not terribly principled about this right now with non-pattern declaration references. You can still reference an unapplied function by its base name alone without its labels, if it's unambiguous:
>>>>>>>>>> 
>>>>>>>>>> func foo(x: Int, y: Int) {}
>>>>>>>>>> 
>>>>>>>>>> let foo_x_y: (Int, Int) -> () = foo
>>>>>>>>>> 
>>>>>>>>>> so it'd be consistent to continue to allow the same in pattern references.
>>>>>>>>> 
>>>>>>>>> WRT ambiguity, do we loose the ability to pattern match on the naked case name when two cases share the same base name?
>>>>>>>>> 
>>>>>>>>> enum Foo {
>>>>>>>>>     case bar(a: Int)
>>>>>>>>>     case bar(b: String)
>>>>>>>>> }
>>>>>>>>> 
>>>>>>>>> switch aFoo {
>>>>>>>>>     case .bar: // matches both cases
>>>>>>>>>         break
>>>>>>>>> }
>>>>>>>>> _______________________________________________
>>>>>>>>> swift-evolution mailing list
>>>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>>> _______________________________________________
>>>>>>>>> swift-evolution mailing list
>>>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>>> 
>>>>>>>> _______________________________________________
>>>>>>>> swift-evolution mailing list
>>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>>> 
>>>>>>> _______________________________________________
>>>>>>> swift-evolution mailing list
>>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>> _______________________________________________
>>>>>> swift-evolution mailing list
>>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>_______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <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/20170125/19a086c2/attachment.html>


More information about the swift-evolution mailing list