> On Sep 8, 2017, at 2:59 PM, Matthew Johnson <[email protected]> wrote:
> 
>> 
>> On Sep 8, 2017, at 2:17 PM, Robert Widmann <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>>> 
>>> On Sep 4, 2017, at 11:35 AM, Matthew Johnson via swift-evolution 
>>> <[email protected] <mailto:[email protected]>> wrote:
>>> 
>>> 
>>>> On Sep 4, 2017, at 11:47 AM, T.J. Usiyan <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>> I wasn't arguing for a strictly parallel syntax. I was arguing against 
>>>> being able to omit labels. I don't view those as strictly tied together. 
>>>> How are they?
>>> 
>>> Like Xiaodi I don’t think it would be productive to rehash the prior 
>>> discussion so I’m going to try to be brief.  
>>> 
>>> In the discussion one idea that arose was to support two labels for 
>>> associated values in a manner similar to parameters.  One would be used 
>>> during construction and the other during matching.  
>>> 
>>> The idea behind this was that when creating a value a case is analagous to 
>>> a factory method and it would be nice to be able provide labels using the 
>>> same naming guidelines we use for external argument labels.  For example, 
>>> if an associated value was an index `at` might be used for clarity at the 
>>> call site.  Labels like this don’t necessarily make as much sense when 
>>> destructuring the value.  The idea of the “internal” label of a case was 
>>> that it would be used when matching and could be elided if the bound name 
>>> was identical.  In the example, `index` might be used.
>> 
>> It’s an interesting idea, but I don’t know of too many cases where I 
>> wouldn’t want the name for destructuring to serve as an API name somehow.  A 
>> function may use labels to aid understanding, flow,  readability, etc. but 
>> an enum case is not necessarily a function-like value, nor does this 
>> proposal want them to be (modulo some internal modeling).
> 
> An enum case is *exactly* analogous to a static factory method or property 
> when it is used to construct values.  It obviously plays a different role in 
> pattern context.
> 
>> 
>>> 
>>> When matching, `let` is interspersed between the label and the name binding.
>> 
>> Good thing this works then
>> 
>> enum Foo {
>>   case foo(x: Int, y: String, z: Float)
>> }
>> 
>> func bar(_ x : Foo) {
>>   switch x {
>>   case let .foo(x: x, y: y, z: z): break
>>   }
>> }
> 
> I consider this syntax to be an anti-pattern.  It can be unclear where a new 
> name is bound and where the value of a pre-existing name is matched.
> 
>> 
>> Even better, John mentioned in the rationale that if the labels ever grow to 
>> be clumsy we can come up with some kind of “ellipses-like” pattern to 
>> indicate we intend to match all the labelled values as they are so named, 
>> etc.  
> 
> It sounds like this would introduce name bindings without the name being 
> explicitly declared.  That works fine where there is a standard pattern such 
> as `oldValue` in a property observer.  I’m not sure I would like it in this 
> context though.
> 
>> Or, and this is the far easier thing that can and should be done today, just 
>> use a struct.
> 
> I generally agree with this advice.
> 
>> 
>>>  Any label is already at a distance from the name it labels.  Instead of 
>>> providing a label the important thing is that the semantic of the bound 
>>> variable be clear at the match site.  Much of the time the label actually 
>>> reduces clarity at a match site by adding verbosity and very often 
>>> repetition.  If the bound name clearly communicates the purpose of the 
>>> associated value a label cannot add any additional clarity, it can only 
>>> reduce clarity.
>> 
>> I disagree.  This would make sense in a world where we didn’t allow 
>> overloading.  But for the purpose of disambiguation, this kind of logic 
>> breaks down.  Say we have this construction
>> 
>> func bar(_ x : Foo) {
>>   switch x {
>>   case let .foo(x, y, z): break
>>   case let .foo(x, y, z, w): break
>>   }
>> }
>> 
>> Without the definition of the original enum, could you tell me what each of 
>> these cases were for, and why they were named so similarly?  Eliding the 
>> label does not enable clarity, it saves keystrokes and enables ambiguous 
>> patterns.
> 
> As stated above, I strongly dislike the syntax that distributes the `let` as 
> I find *that* to be unclear.  Let’s rewrite that example:
> 
> func bar(_ x : Foo) {
>   switch x {
>   case .foo(let x, let y, let z): break
>   case .foo(let x, let y, let z, let w): break
>   }
> }
> 
> Now I can see exactly where names are being bound.  I know if a label exists 
> it matches the name that is bound.  If two distinct cases might be matched I 
> would expect a compiler error.  For example, if Foo was defined as follows 
> the above switch to produce an error on the second pattern but not the first:
> 
> enum Foo {
>   case foo(x: Int, y: String, z: Float)
>   case foo(x: Int, y: String, z: Float, s: String)
>   case foo(x: Int, y: String, z: Float, w: Double)
> }
> 
> If the proposal had been accepted without the modification I would not find 
> the above switch ambiguous in behavior although I admit that it carries more 
> potential for mistake than a design that requires explicit labels to 
> disambiguate an overloaded base name.
> 
>> 
>>> 
>>> The proposal acknowledges most of this by allowing us to elide labels when 
>>> the bound name matches the label. 
>> 
>> That part of the proposal was not accepted which is part of why I’m bringing 
>> this up at all.  
> 
> I thought the part that elides labels was accepted with the modification that 
> this only applies when the base name is unambiguous.  I suspect overloaded 
> base names will be relatively rare so I think the case of unambiguous base 
> names is by far the most important.  I think the rationale given is pretty 
> good.

I’ll quote the rationale (minus some extraneous text in the examples):

> A case pattern may omit labels for the associated values of a case if there 
> is only one case with the same base name and arity.  A pattern must omit all 
> labels if it omits any of them; thus, a case pattern either exactly matches 
> the full name of a case or has no labels at all.  For example:

 enum E {
        // ...
    case many(value: Int)
    case many(first: Int, second: Int)
    case many(alpha: Int, beta: Int)
        // ...
  }

  // Valid: there is only one case with this base name and payload count.
  case .many(let a):

If we’re matching by arity, the labels go out the door; this part of the 
proposal was not accepted.

~Robert Widmann

> 
>> Besides, it wouldn’t have worked quite the way the authors intended.
>> 
>> enum Foo {
>>   case foo(x: Int, x: String)
>> }
>> 
>> func bar(_ x : Foo) {
>>   switch x {
>>   // We wanted to avoid labels, but instead we would be required to redeclare
>>   // 'x' in this pattern which forces the use of labels to allow a different 
>> bound name.
>>   case let .foo(x, x): break
>>  }
>> }  
> 
> This would of course need to be rejected because it attempts to bind the same 
> name twice.  I don’t think anyone intended for this to work.
> 
>> 
>> ~Robert Widmann
>> 
>>> 
>>>> 
>>>> On Mon, Sep 4, 2017 at 12:38 PM, Matthew Johnson <[email protected] 
>>>> <mailto:[email protected]>> wrote:
>>>> 
>>>>> On Sep 4, 2017, at 10:52 AM, T.J. Usiyan via swift-evolution 
>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>> 
>>>>> While re-litigating has it's issues, I am for simplifying the rule and 
>>>>> always requiring the labels if they exist. This is similar to the change 
>>>>> around external labels. Yes, it is slightly less convenient, but it 
>>>>> removes a difficult to motivate caveat for beginners.
>>>> 
>>>> I disagree.  Creating a value and destructuring it are two very different 
>>>> operations and I believe it is a mistake to require them to have parallel 
>>>> syntax.  
>>>> 
>>>> Imagine a future enhancement to the language that supports destructuring a 
>>>> struct.  A struct might not have a strictly memberwise initializer.  It 
>>>> might not even be possible to reconstruct initializer arguments for the 
>>>> sake of parallel destructuring syntax.  There might even be more than one 
>>>> projection that is reasonable to use when destructuring the value in a 
>>>> pattern (such as cartesian and polar coordinates).
>>>> 
>>>> FWIW, I made this case in more detail during the discussion and review of 
>>>> this proposal.
>>>> 
>>>>> 
>>>>> On Sun, Sep 3, 2017 at 4:35 PM, Xiaodi Wu via swift-evolution 
>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>> The desired behavior was the major topic of controversy during review; 
>>>>> I’m wary of revisiting this topic as we are essentially relitigating the 
>>>>> proposal.
>>>>> 
>>>>> To start off, the premise, if I recall, going into review was that the 
>>>>> author **rejected** the notion that pattern matching should mirror 
>>>>> creation. I happen to agree with you on this point, but it was not the 
>>>>> prevailing argument. Fortunately, we do not need to settle this to arrive 
>>>>> at some clarity for the issues at hand.
>>>>> 
>>>>> From a practical standpoint, a requirement for labels in all cases would 
>>>>> be much more source-breaking, whereas the proposal as it stands would 
>>>>> allow currently omitted labels to continue being valid. Moreover, and I 
>>>>> think this is a worthy consideration, one argument for permitting the 
>>>>> omission of labels during pattern matching is to encourage API designers 
>>>>> to use labels to clarify initialization without forcing its use by API 
>>>>> consumers during every pattern matching operation.
>>>>> 
>>>>> In any case, the conclusion reached is precedented in the world of 
>>>>> functions:
>>>>> 
>>>>> func g(a: Int, b: Int) { ... }
>>>>> let f = g
>>>>> f(1, 2)
>>>>> 
>>>>> On Sun, Sep 3, 2017 at 15:13 Robert Widmann via swift-evolution 
>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>> Hello Swift Evolution,
>>>>> 
>>>>> I took up the cause of implementing SE-0155 
>>>>> <https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md>,
>>>>>  and am most of the way through the larger points of the proposal.  One 
>>>>> thing struck me when I got to the part about normalizing the behavior of 
>>>>> pattern matching 
>>>>> <https://github.com/apple/swift-evolution/blob/master/proposals/0155-normalize-enum-case-representation.md#pattern-consistency>.
>>>>>   The Core Team indicated in their rationale 
>>>>> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035972.html>
>>>>>  that the proposal’s suggestion that a variable binding sub in for a 
>>>>> label was a little much as in this example:
>>>>> 
>>>>> enum Foo {
>>>>>   case foo(x: Int, y: Int)
>>>>> }
>>>>> if case let .foo(x: x, y: y) {} // Fine!  Labels match and are in order
>>>>> if case let .foo(x, y: y) {} // Bad!  Missing label 'x'
>>>>> if case let .foo(x, y) {} // Fine?  Missing labels, but variable names 
>>>>> match labels
>>>>> 
>>>>> They instead suggested the following behavior:
>>>>> 
>>>>> enum Foo {
>>>>>   case foo(x: Int, y: Int)
>>>>> }
>>>>> if case let .foo(x: x, y: y) {} // Fine!  Labels match and are in order
>>>>> if case let .foo(x, y: y) {} // Bad!  Missing label 'x'
>>>>> if case let .foo(x, y) {} // Fine?  Missing labels, and full name of case 
>>>>> is unambiguous
>>>>> 
>>>>> Which, for example, would reject this:
>>>>> 
>>>>> enum Foo {
>>>>>   case foo(x: Int, y: Int) // Note: foo(x:y:)
>>>>>   case foo(x: Int, z: Int) // Note: foo(x:z:)
>>>>> }
>>>>> if case let .foo(x, y) {} // Bad!  Are we matching foo(x:y:) or foo(x:z:)?
>>>>> 
>>>>> With this reasoning:
>>>>> 
>>>>>>  - While an associated-value label can indeed contribute to the 
>>>>>> readability of the pattern, the programmer can also choose a meaningful 
>>>>>> name to bind to the associated value.  This binding name can convey at 
>>>>>> least as much information as a label would.
>>>>>> 
>>>>>>   - The risk of mis-labelling an associated value grows as the number of 
>>>>>> associated values grows.  However, very few cases carry a large number 
>>>>>> of associated values.  As the amount of information which the case 
>>>>>> should carry grows, it becomes more and more interesting to encapsulate 
>>>>>> that information in its own struct — among other reasons, to avoid the 
>>>>>> need to revise every matching case-pattern in the program.  Furthermore, 
>>>>>> when a case does carry a significant number of associated values, there 
>>>>>> is often a positional conventional between them that lowers the risk of 
>>>>>> re-ordering: for example, the conventional left-then-right ordering of a 
>>>>>> binary search tree.  Therefore this risk is somewhat over-stated, and of 
>>>>>> course the programmer should remain free to include labels for cases 
>>>>>> where they feel the risk is significant.
>>>>>> 
>>>>>>   - It is likely that cases will continue to be predominantly 
>>>>>> distinguished by their base name alone.  Methods are often distinguished 
>>>>>> by argument labels because the base name identifies an entire class of 
>>>>>> operation with many possible variants.  In contrast, each case of an 
>>>>>> enum is a kind of data, and its name is conventionally more like the 
>>>>>> name of a property than the name of a method, and thus likely to be 
>>>>>> unique among all the cases.  Even when cases are distinguished using 
>>>>>> only associated value labels, it simply means that the corresponding 
>>>>>> case-patterns must include those labels; we should not feel required to 
>>>>>> force that burden on all other case-patterns purely to achieve 
>>>>>> consistency with this presumably-unusual style.
>>>>>> Accordingly, while it needs to be possible to include associated value 
>>>>>> labels in a case-pattern, and in some situations it may be wise to 
>>>>>> include them, the core team believes that requiring associated value 
>>>>>> labels would be unduly onerous.
>>>>> 
>>>>> 
>>>>> This sounds fine in principle, but I believe it is inconsistent with the 
>>>>> goals of the proposal and doesn’t actually normalize much about the 
>>>>> existing pattern matching process.  As it stands, labels may be omitted 
>>>>> from patterns because Swift’s philosophy before this proposal is that 
>>>>> associated values in enum cases were conceptually tuples.  With the 
>>>>> addition of default arguments, the ability to overload case names with 
>>>>> differing associated value labels, and making the labels part of the API 
>>>>> name, there is no reason we should allow tuple-like behavior in just this 
>>>>> one case.
>>>>> 
>>>>>> While an associated-value label...
>>>>> 
>>>>> While it is true that a user often has a domain-specific intention for 
>>>>> variables created during the destructuring process, the labels do not 
>>>>> distract from the original purpose of the API and the user is still free 
>>>>> to provide whatever name they see fit.
>>>>> 
>>>>>> Therefore this risk is somewhat over-stated, and of course the 
>>>>>> programmer should remain free to include labels for cases where they 
>>>>>> feel the risk is significant...
>>>>> 
>>>>> This is phrased as a matter of choice, in practice this is perplexing.  
>>>>> Recall an earlier rejected pattern:
>>>>> 
>>>>> enum Foo {
>>>>>   case foo(x: Int, y: Int)
>>>>> }
>>>>> if case let .foo(x, y: y) {} // Bad!  Missing label ‘x'
>>>>> 
>>>>> From the user’s perspective, it is obvious what should happen: Either 
>>>>> they did, or did not, intend to match labels.  From the compiler’s 
>>>>> perspective this is a proper ambiguity.  Did the user intend to provide a 
>>>>> “more meaningful name” and hence meant to elide the label, or did the 
>>>>> user intend to match all the labels but forgot or deleted one?  It is not 
>>>>> obvious why, if we’re making the distinction, we should assume one way or 
>>>>> the other.   This case only gets worse when we must diagnose intent if 
>>>>> the case is also overloaded by base name.
>>>>> 
>>>>> I don’t see how it is "unduly onerous” to teach code completion to 
>>>>> suggest the full name of an enum case everywhere or to create diagnostics 
>>>>> that always insert missing labels in patterns to correct the user’s 
>>>>> mistake.  Freedom of choice is, in this case, only making a hard problem 
>>>>> harder.
>>>>> 
>>>>>> It is likely that cases will continue to be predominantly distinguished 
>>>>>> by their base name alone...
>>>>> 
>>>>> This makes sense given the current state of the world, but under this 
>>>>> proposal we fully expect users to be overloading that base name and 
>>>>> writing more and more ambiguous patterns.  We should encourage 
>>>>> disambiguating these cases with labels as a matter of both principle and 
>>>>> QoI.  
>>>>> 
>>>>> A pattern is meant to mirror the way a value was constructed with 
>>>>> destructuring acting as a dual to creation.  By maintaining the structure 
>>>>> of the value in the pattern, labels included, users can properly convey 
>>>>> that they intend the label to be a real part of the API of an enum case 
>>>>> with associated values instead of just an ancillary storage area.  
>>>>> Further, we can actually simplify pattern matching by making enum cases 
>>>>> consistent with something function-like instead of tuple-like.
>>>>> 
>>>>> To that end, I'd like the rationale and the proposal to be amended to 
>>>>> require labels in patterns in all cases.
>>>>> 
>>>>> Thoughts?
>>>>> 
>>>>> ~Robert Widmann
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>> 
>>>> 
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to