> 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