To add more real-world project data to this discussion, I just did a test
migration of Siesta to Swift 4 using the 2017-05-30 snapshot.
Nothing earth-shattering follows — just more practical evidence that the
problem needs attention.
Here’s what I found:
(1) The lack of tuple destructing under discussion here hit the project in half
a dozen places. That’s in only a ~2k-line codebase.
(2) Every one of those places it hit involved a Dictionary.
Most involved calling map, flatMap, or filter, so adding Dictionary-specific
2-arg flavors of those methods to stdlib would reduce the burden of SE-110.
However, some places involved custom Collection extensions (e.g. “any”), so
it’s not just the stdlib that would have to add nearly-redundant
dictionary-specific variants of Collection methods. Destructuring still has a
clear advantage.
(3) All the problems were indeed fixable using $0.0 and $0.1 in place of named
destructured args, but the damage to readability was … severe. Compare this,
for example:
let nonEmptyStages = stages
.filter { _, stage in !stage.isEmpty }
.map { key, _ in key }
…to this:
let nonEmptyStages = stages
.filter { !$0.1.isEmpty }
.map { $0.0 }
Even though “stage” conveys little information, and “key” even less, the net
gain from those named args is large, at least to my eyes.
(4) SE-110 is also the thing that prevents Siesta’s dependencies from compiling.
Hope all this helps weigh the tradeoffs of taking this on.
Cheers,
Paul
> On Jun 1, 2017, at 2:32 PM, Vladimir.S via swift-evolution
> <[email protected]> wrote:
>
> On 01.06.2017 19:31, Tommaso Piazza wrote:
>> Dear all,
>> I made a comparison of Swift's 4 lack of tuple unsplatting, here is how it
>> stands in comparison with other languages
>> https://gist.github.com/blender/53f9568617654c38a219dd4a8353d935
>
> Thank you! Very useful information. And also I really like the opinion of
> @AliSoftware in comments for this article.
>
> I'd suggest to add this variant to Swift section in your article:
>
> let eighteenOrMore = ["Tom" : 33, "Rebecca" : 17, "Siri" : 5].filter {
> (arg: (name: String, age: Int)) in arg.age >= 18 }
>
> (I believe it is better that 2 others Swift variants.)
>
> It seems for me that we need to allow some special syntax for *explicit*
> tuple destructuring in closures to make all happy.
>
> FWIW These suggestions are my favorite:
>
> 1. Just allow type inference for tuple's destructured variables in this
> position:
>
> .filter { (arg: (name, age)) in arg.age >= 18 }
>
>
> 2. (1) + allow underscore for tuple argument name:
>
> .filter { (_: (name, age)) in age >= 18 }
>
>
> 3. (2) + allow to omit parenthesis (probably only in case of just one tuple
> argument)
>
> .filter { _: (name, age) in age >= 18 }
>
>
> 4. Use pattern matching syntax:
>
> .filter { case let (name, age) in age >= 18 }
>
> (looks similar as allowed today: if case let (name, age) = x { print(name,
> age) } )
>
>
> 5. Use two pairs of parenthesis :
>
> .filter { ((name, age)) in age >= 18 }
>
> Btw, about the 5th variant. If took what is allowed today:
> .filter { (arg: (name: String, age: Int)) in arg.age >= 18 }
> , and allow type inference for tuple part arguments, we'll have this:
> .filter { (arg: (name, age)) in arg.age >= 18 }
> , and if additionally allow skipping of tuple argument declaration we'll have:
> .filter { ((name, age)) in arg.age >= 18 }
> I.e. two pairs for parenthesis for tuple destructuring, and such syntax is
> similar to the type this closure should have : ((String, Int)) -> Bool
>
>
>> On Thursday, June 1, 2017 12:25 PM, Vladimir.S via swift-evolution
>> <[email protected]> wrote:
>> On 01.06.2017 0:42, John McCall wrote:
>> >> On May 31, 2017, at 2:02 PM, Stephen Celis <[email protected]
>> >> <mailto:[email protected]>> wrote:
>> >>> On May 28, 2017, at 7:04 PM, John McCall via swift-evolution
>> >>> <[email protected] <mailto:[email protected]>> wrote:
>> >>>
>> >>> Yes, I agree. We need to add back tuple destructuring in closure
>> >>> parameter
>> >>> lists because this is a serious usability regression. If we're
>> >>> reluctant to
>> >>> just "do the right thing" to handle the ambiguity of (a,b), we should at
>> >>> least
>> >>> allow it via unambiguous syntax like ((a,b)). I do think that we should
>> >>> just
>> >>> "do the right thing", however, with my biggest concern being whether
>> >>> there's
>> >>> any reasonable way to achieve that in 4.0.
>> >>
>> >> Closure parameter lists are unfortunately only half of the equation here.
>> >> This
>> >> change also regresses the usability of point-free expression.
>> >
>> > The consequences for point-free style were expected and cannot really be
>> > eliminated without substantially weakening SE-0110. Closure convenience
>> > seems to
>> > me to be a much more serious regression.
>> John, do you also want to say "and without weakening SE-0066"? Because, if I
>> understand correctly, in this case:
>> func add(_ x: Int, _ y: Int) -> Int {
>> return x + y
>> }
>> zip([1, 2, 3], [4, 5, 6]).map(add)
>> .. we have a clear function type mismatch situation, when map() expects
>> function of
>> type ((Int, Int))->Int, but function of type (Int,Int)->Int is provided ? So
>> probably
>> the additional 'reason' of the 'problem' in this case is SE-0066, no?
>> Or I don't understand the SE-0066 correctly..
>> Do we want to allow implicit conversions between function type
>> ((Int,Int))->Int and
>> (Int,Int)->Int?
>> Quote from SE-0066:
>> ---
>> (Int, Int) -> Int // function from Int and Int to Int
>> ((Int, Int)) -> Int // function from tuple (Int, Int) to Int
>> ---
>> During this discussion I see a wish of some group of developers to just
>> return back
>> tuple splatting for function/closure arguments, so they can freely send
>> tuple to
>> function/closure accepting a list of parameters(and probably vise-versa).
>> Is it worth to follow SE-0066 and SE-0110 as is, i.e. disallow tuple
>> deconstructing
>> and then, as additive change improve the situation with tuple
>> splatting/deconstructing later with separate big proposal?
>> Btw, about the SE-0110 proposal. It was discussed, formally reviewed and
>> accepted. I
>> expect that its revision also should be formally proposed/reviewed/accepted
>> to
>> collect a wide range of opinions and thoughts, and attract the attention of
>> developers in this list to the subject.
>> Also, if we revisit SE-0110, will this code be allowed?:
>> func foo(_ callback: ((Int,Int))->Void) {}
>> let mycallback = {(x:Int, y:Int)->Void in }
>> foo(mycallback)
>> and
>> func foo(_ callback: (Int,Int)->Void) {}
>> let mycallback = {(x: (Int, Int))->Void in }
>> foo(mycallback)
>> If so, what will be result of this for both cases? :
>> print(type(of:mycallback)) // (Int,Int)->Void or ((Int,Int))->Void
>> If allowed, do we want to allow implicit conversion between types
>> (Int,Int)->Void and
>> ((Int,Int))->Void in both directions? (Hello tuple splatting?)
>> >
>> > John.
>> >
>> >
>> >>
>> >> func add(_ x: Int, _ y: Int) -> Int { return x + y }
>> >>
>> >> zip([1, 2, 3], [4, 5, 6]).map(add)
>> >>
>> >> // error: nested tuple parameter '(Int, Int)' of function '(((_.Element,
>> >> _.Element)) throws -> _) throws -> [_]' does not support destructuring
>> >>
>> >> This may not be a common pattern in most projects, but we heavily use
>> >> this style
>> >> in the Kickstarter app in our functional and FRP code. Definitely not the
>> >> most
>> >> common coding pattern, but a very expressive one that we rely on.
>> >>
>> >> Our interim solution is a bunch of overloaded helpers, e.g.:
>> >>
>> >> func tupleUp<A, B, C>(_ f: (A, B) -> C) -> ((A, B)) -> C { return }
>> >>
>> >> zip([1, 2, 3], [4, 5, 6]).map(tupleUp(add))
>> >>
>> >> Stephen
>> >
>> > .
>> >
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected] <mailto:[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