Sent from my iPad

> On Jun 17, 2017, at 10:20 AM, Paul Cantrell via swift-evolution 
> <[email protected]> wrote:
> 
> Yes, agreed, the fix for Chris’s brain-bender shouldn’t revisit any of 
> SE-0155’s matching & labeling rules.
> 
> How about:
> 
> 1. Disallow labels for bare tuples in patterns. (By “bare tuples” I mean “not 
> representing associated values on an enum.”)
> 
>       let (a: x, b: y) = foo  // disallowed
>       let (x, y) = foo  // OK
> 
> 2. Maybe require “let” before individual identifiers when pattern matching on 
> associated values, while preserving SE-0155’s rules for when labels may 
> appear:
> 
>       case let .foo(a: x, b: y)  // disallowed
>       case .foo(a: let x, b: let y)  // OK
> 
> #2 is debatable. It would solve an enum-based parallel to Chris’s original:
> 
>       case let .foo(a: Int, b: String)  // disallowed
>       case .foo(a: let Int, b: let String)  // allowed, and Int/String no 
> longer look like types
> 

Doing this with #2 is what I suggested earlier.  I like this because I find the 
disallowed style to have too much cognitive load anyway.

> P
> 
>> On Jun 16, 2017, at 10:55 PM, Xiaodi Wu <[email protected]> wrote:
>> 
>> See:
>> https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035972.html
>> 
>> 
>>> On Fri, Jun 16, 2017 at 22:32 Paul Cantrell <[email protected]> wrote:
>>> Under these not-yet-implemented plans, if associated value labels are no 
>>> longer tuple labels, then how will pattern matching work? And what existing 
>>> pattern matching code will break / continue to work?
>>> 
>>> P
>>> 
>>>> On Jun 16, 2017, at 10:22 PM, Xiaodi Wu <[email protected]> wrote:
>>>> 
>>>> Keep in mind that once the latest proposal about enum cases is 
>>>> implemented, these will be at least notionally no longer tuple labels but 
>>>> rather a sugared way of spelling part of the case name. The rules 
>>>> surrounding labels during case matching have only just been revised and 
>>>> approved and have not even yet been implemented. I don’t think it would be 
>>>> wise to fiddle with them again.
>>>> 
>>>> 
>>>> On Fri, Jun 16, 2017 at 21:21 Paul Cantrell <[email protected]> wrote:
>>>>>>> On Jun 16, 2017, at 5:23 PM, Mark Lacey <[email protected]> wrote:
>>>>>>> 
>>>>>>> 
>>>>>>>> On Jun 16, 2017, at 2:09 PM, Paul Cantrell <[email protected]> wrote:
>>>>>>>> 
>>>>>>>> 
>>>>>>>>> On Jun 16, 2017, at 3:43 PM, Mark Lacey <[email protected]> wrote:
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>>> On Jun 16, 2017, at 1:21 PM, Mark Lacey <[email protected]> wrote:
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>>> On Jun 16, 2017, at 11:13 AM, Paul Cantrell via swift-evolution 
>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>> On Jun 15, 2017, at 7:17 PM, Xiaodi Wu via swift-evolution 
>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>> 
>>>>>>>>>>> 
>>>>>>>>>>>> On Thu, Jun 15, 2017 at 19:03 Víctor Pimentel 
>>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>>>> On 16 Jun 2017, at 01:55, Xiaodi Wu via swift-evolution 
>>>>>>>>>>>>> <[email protected]> wrote:
>>>>>>>>>>>>> 
>>>>>>>>>>>>>> On Thu, Jun 15, 2017 at 17:43 David Hart <[email protected]> 
>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>> 
>>>>>>>>>>>>>> By the way, I’m not attempting to deduce that nobody uses this 
>>>>>>>>>>>>>> feature by the fact I didn’t know about it. But I think it’s one 
>>>>>>>>>>>>>> interesting datapoint when comparing it to SE-0110.
>>>>>>>>>>>>> 
>>>>>>>>>>>>> 
>>>>>>>>>>>>> SE-0110, **in retrospect**, has had impacts on a lot of users; 
>>>>>>>>>>>>> prospectively, it was thought to be a minor change, even after 
>>>>>>>>>>>>> review and acceptance.
>>>>>>>>>>>>> 
>>>>>>>>>>>>> Keep in mind that this proposed change would also eliminate 
>>>>>>>>>>>>> inline tuple shuffle. For instance, the following code will cease 
>>>>>>>>>>>>> to compile:
>>>>>>>>>>>>> 
>>>>>>>>>>>>> let x = (a: 1.0, r: 0.5, g: 0.5, b: 0.5)
>>>>>>>>>>>>> func f(color: (r: Double, g: Double, b: Double, a: Double)) {
>>>>>>>>>>>>>   print(color)
>>>>>>>>>>>>> }
>>>>>>>>>>>>> f(color: x)
>>>>>>>>>>>>> 
>>>>>>>>>>>>> It is an open question how frequently this is used. But like 
>>>>>>>>>>>>> implicit tuple destructuring, it currently Just Works(TM) and 
>>>>>>>>>>>>> users may not realize they’re making use of the feature until 
>>>>>>>>>>>>> it’s gone.
>>>>>>>>>>>> 
>>>>>>>>>>>> It's much much less used, by looking at open source projects I 
>>>>>>>>>>>> doubt that a significant portion of projects would have to change 
>>>>>>>>>>>> code because of this.
>>>>>>>>>>> 
>>>>>>>>>>> The reason that I’m urging caution is because, if I recall 
>>>>>>>>>>> correctly, that is also what we said about SE-0110 on this list. 
>>>>>>>>>>> Then, as now, we were discussing an issue with something left over 
>>>>>>>>>>> from the Swift 1 model of tuples. Then, as now, we believed that 
>>>>>>>>>>> the feature in question was rarely used. Then, as now, we believed 
>>>>>>>>>>> that removing that feature would improve consistency in the 
>>>>>>>>>>> language, better both for the compiler and for users. Then, as now, 
>>>>>>>>>>> leaving it in was thought to prevent moving forward with other 
>>>>>>>>>>> features that could improve Swift.
>>>>>>>>>> 
>>>>>>>>>> Data:
>>>>>>>>>> 
>>>>>>>>>> I hacked up a regexp that will catch most uses of labeled tuples in 
>>>>>>>>>> pattern matches, e.g. “let (foo: bar) = baz”. That’s what we’re 
>>>>>>>>>> talking about, right?
>>>>>>>>> 
>>>>>>>>> That’s the obvious example that people find confusing.
>>>>>>>>> 
>>>>>>>>> Less obvious places that labeled tuple patterns show up are ‘case 
>>>>>>>>> let’ and ‘case’ (see below). 
>>>>>>>> 
>>>>>>>> Okay, I should have looked at your regex and read further. It looks 
>>>>>>>> like you were already trying to match these.
>>>>>>> 
>>>>>>> I did walk the grammar for all occurrences of _pattern_.
>>>>>>> 
>>>>>>> I’m only matching named tuple patterns that immediately follow one of 
>>>>>>> the keywords which a pattern follows (for, case, let, var, and catch). 
>>>>>>> As I mentioned, I’m not matching patterns that come later in 
>>>>>>> comma-separated lists. I’m also not matching named tuples inside nested 
>>>>>>> patterns, e.g. let ((a: b), (c: d)).
>>>>>>> 
>>>>>>> But again, if even the most basic form of this construct is so rare, I 
>>>>>>> doubt more robust matching would turn up that much more usage.
>>>>>>> 
>>>>>>>> I’m surprised you’re not seeing any uses of ‘case’ with labels.
>>>>>>> 
>>>>>>> Me too. But I just verified that my pattern does match them.
>>>>>> 
>>>>>> Are you sure? It doesn’t look like it’s going to match the example I 
>>>>>> gave due to the leading ‘.’ on the enum case.
>>>>> 
>>>>> Ah! I should have read your original message more carefully. You’re quite 
>>>>> right, I only was checking case statements for raw tuples like this:
>>>>> 
>>>>>     case let (i: a, f: b):
>>>>> 
>>>>> …and not for anything involving associated values. I hadn’t even 
>>>>> considered that associated values would be affected by this, but looking 
>>>>> at the grammar it seems they would indeed be.
>>>>> 
>>>>> Another clumsy regex search, this time for patterns with tuple labels on 
>>>>> associated values, turned up 111 results (one per ~3800 lines). Not super 
>>>>> common, but certainly nothing to sneeze at. Here they are:
>>>>> 
>>>>>     https://gist.github.com/pcantrell/d32cdb5f7db6d6626e45e80011163efb
>>>>> 
>>>>> Looking through that gist, these usages mostly strike me as being just 
>>>>> fine:
>>>>> 
>>>>>     case .cover(from: .bottom):
>>>>> 
>>>>>     case .reference(with: let ref):
>>>>> 
>>>>>     case .update(tableName: let tableName, columnNames: _):
>>>>> 
>>>>> I’d even say that removing the tuple labels would make things worse. 
>>>>> Consider:
>>>>> 
>>>>>     case .name(last: let firstName, first: _):  // mistake is clear
>>>>>     case .name(let firstName, _):               // mistake is buried
>>>>> 
>>>>> In Chris’s original brain-bending example, the confusion is that there’s 
>>>>> no “let” after the colon, so Int and Float look like types instead of 
>>>>> variable names:
>>>>> 
>>>>>     let (a : Int, b : Float) = foo()
>>>>> 
>>>>> However, in the examples in the gist above, most of the patterns either 
>>>>> (1) declare variables using a `let` after the colon:
>>>>> 
>>>>>     case .reference(with: let ref):
>>>>> 
>>>>> …or (2) don’t declare a variable at all:
>>>>> 
>>>>>     case .string(format: .some(.uri)):
>>>>> 
>>>>> What if we allowed labels on associated values, but required a `let` 
>>>>> after the colon to bind a variable?
>>>>> 
>>>>>     case let .a(b: c):  // disallowed
>>>>>     case .a(b: let c):  // OK
>>>>> 
>>>>> Only 15 of those 111 run afoul of _that_ rule. Here they are:
>>>>> 
>>>>>     https://gist.github.com/pcantrell/9f61045d7d7c8d18eeec8ebbef6cd8f8
>>>>> 
>>>>> That’s one breakage every ~28000 lines, which seems much more acceptable. 
>>>>> The drawback is that you can’t declare variables for a bunch of 
>>>>> associated value en masse anymore; you need one let per value. (See line 
>>>>> 2 in that gist.)
>>>>> 
>>>>>> You might want to try the patch I sent as it will definitely catch any 
>>>>>> tuple pattern that makes it to the verifier and does have labels.
>>>>> 
>>>>> I’m not set up to build the compiler, unfortunately. One of these days.
>>>>> 
>>>>> P
>>>>> 
>>>>>> 
>>>>>> Mark
>>>>>> 
>>>>>>> 
>>>>>>> P
>>>>>>> 
>>>>>>>> 
>>>>>>>> Mark
>>>>>>>> 
>>>>>>>>> Fortunately we do not appear to allow shuffling in these cases. I’m 
>>>>>>>>> not sure if the human disambiguation is easier here because of the 
>>>>>>>>> context (‘case let’ and ‘case’), but I don’t recall seeing complain 
>>>>>>>>> about these being confusing (having said that it’s entirely possible 
>>>>>>>>> they are very confusing the first time someone sees them, in 
>>>>>>>>> particular ‘cast let’ and the binding form of ‘case’.
>>>>>>>>> 
>>>>>>>>> enum X {
>>>>>>>>>   case e(i: Int, f: Float)
>>>>>>>>> }
>>>>>>>>> 
>>>>>>>>> let x = X.e(i: 7, f: 12)
>>>>>>>>> 
>>>>>>>>> if case let X.e(i: hi, f: bye) = x {
>>>>>>>>>   print("(i: \(hi), f: \(bye))")
>>>>>>>>> }
>>>>>>>>> 
>>>>>>>>> func test(_ x: X, _ a: Int, _ b: Float) {
>>>>>>>>>   switch x {
>>>>>>>>>   case .e(i: a, f: b):
>>>>>>>>>     print("match values")
>>>>>>>>>   case .e(i: let _, f: let _):
>>>>>>>>>     print("bind values")
>>>>>>>>>   default:
>>>>>>>>>     break
>>>>>>>>>   }
>>>>>>>>> }
>>>>>>>>> 
>>>>>>>>> test(X.e(i: 1, f: 2), 1, 2)
>>>>>>>>> test(X.e(i: 1, f: 2), 3, 4)
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> I ran that against all 55 projects in swift-source-compat-suite, 
>>>>>>>>>> comprising about over 400,000 lines of Swift code, and found … 
>>>>>>>>>> drumroll … exactly one match:
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> neota (swift-source-compat-suite)$ find project_cache -name 
>>>>>>>>>> '*.swift' -print0 | xargs -0 pcregrep -M 
>>>>>>>>>> '(for|case|let|var|catch)\s+\([a-zA-Z0-9_]+\s*:'
>>>>>>>>>> project_cache/RxSwift/RxExample/RxExample-iOSTests/TestScheduler+MarbleTests.swift:
>>>>>>>>>>                 let (time: _, events: events) = 
>>>>>>>>>> segments.reduce((time: 0, events: [RecordedEvent]())) { state, event 
>>>>>>>>>> in
>>>>>>>>>> 
>>>>>>>>>> 
>>>>>>>>>> Caveats about this method:
>>>>>>>>>> 
>>>>>>>>>> • My regexp won’t match second and third patterns in a 
>>>>>>>>>> comma-separated let or case, e.g.:
>>>>>>>>>> 
>>>>>>>>>>    let a = b, (c: d) = e
>>>>>>>>>> 
>>>>>>>>>> • It doesn’t match non-ascii identifiers.
>>>>>>>>>> 
>>>>>>>>>> • This experiment only considers labeled tuples in pattern matches, 
>>>>>>>>>> what I took Chris’s original puzzler to be about. Label-based tuple 
>>>>>>>>>> shuffling is a separate question.
>>>>>>>>>> 
>>>>>>>>>> Still, even if it’s undercounting slightly, one breakage in half a 
>>>>>>>>>> million lines of code should put to rest concerns about unexpected 
>>>>>>>>>> widespread impact.
>>>>>>>>>> 
>>>>>>>>>> (Anything else I’m missing?)
>>>>>>>>>> 
>>>>>>>>>> • • •
>>>>>>>>>> 
>>>>>>>>>> Aside for those who know the tools out there: what would it take to 
>>>>>>>>>> run inspections like this against ASTs instead of using a regex? 
>>>>>>>>>> Could we instrument the compiler as Brent suggested?
>>>>>>>>> 
>>>>>>>>> If you want to catch *all* of these cases then the patch below will 
>>>>>>>>> do it by failing the AST verifier when it hits a pattern with labels. 
>>>>>>>>> If you only want to find the plain let-binding versions of this and 
>>>>>>>>> not the ‘case let’ and ‘case’ ones, I’d suggest looking at the parser 
>>>>>>>>> to see if there’s an easy place to instrument (I don’t know offhand).
>>>>>>>>> 
>>>>>>>>> Mark
>>>>>>>>> 
>>>>>>>>> diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp
>>>>>>>>> index b59a7ade23..ba4b2a245d 100644
>>>>>>>>> --- a/lib/AST/ASTVerifier.cpp
>>>>>>>>> +++ b/lib/AST/ASTVerifier.cpp
>>>>>>>>> @@ -2772,6 +2772,13 @@ public:
>>>>>>>>>      }
>>>>>>>>>  
>>>>>>>>>      void verifyParsed(TuplePattern *TP) {
>>>>>>>>> +      for (auto &elt : TP->getElements()) {
>>>>>>>>> +        if (!elt.getLabel().empty()) {
>>>>>>>>> +          Out << "Labeled tuple patterns are offensive!\n";
>>>>>>>>> +          abort();
>>>>>>>>> +        }
>>>>>>>>> +      }
>>>>>>>>> +
>>>>>>>>>        PrettyStackTracePattern debugStack(Ctx, "verifying 
>>>>>>>>> TuplePattern", TP);
>>>>>>>>>        verifyParsedBase(TP);
>>>>>>>>>      }
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>> 
>>>>>>>>>> Or can SourceKit / SourceKitten give a full AST? Or has anybody 
>>>>>>>>>> written a Swift parser in Swift?
>>>>>>>>>> 
>>>>>>>>>> Cheers,
>>>>>>>>>> 
>>>>>>>>>> Paul
>>>>>>>>>> 
>>>>>>>>>> _______________________________________________
>>>>>>>>>> swift-evolution mailing list
>>>>>>>>>> [email protected]
>>>>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>>> 
>>> 
> 
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> 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