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] > <mailto:[email protected]>> wrote: >> On Jun 16, 2017, at 5:23 PM, Mark Lacey <[email protected] >> <mailto:[email protected]>> wrote: >> >> >>> On Jun 16, 2017, at 2:09 PM, Paul Cantrell <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>>> >>>> On Jun 16, 2017, at 3:43 PM, Mark Lacey <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>> >>>>> On Jun 16, 2017, at 1:21 PM, Mark Lacey <[email protected] >>>>> <mailto:[email protected]>> wrote: >>>>> >>>>>> >>>>>> On Jun 16, 2017, at 11:13 AM, Paul Cantrell via swift-evolution >>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>> >>>>>>> >>>>>>> On Jun 15, 2017, at 7:17 PM, Xiaodi Wu via swift-evolution >>>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>>> >>>>>>> >>>>>>> On Thu, Jun 15, 2017 at 19:03 Víctor Pimentel <[email protected] >>>>>>> <mailto:[email protected]>> wrote: >>>>>>> On 16 Jun 2017, at 01:55, Xiaodi Wu via swift-evolution >>>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>>> >>>>>>>> On Thu, Jun 15, 2017 at 17:43 David Hart <[email protected] >>>>>>>> <mailto:[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 > <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 > <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] <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
