[swift-evolution] Proposal: Always flatten the single element tuple

Vladimir.S svabox at gmail.com
Thu Jun 8 17:51:10 CDT 2017


On 09.06.2017 0:31, Mark Lacey wrote:
> 
>> On Jun 8, 2017, at 2:05 PM, Vladimir.S via swift-evolution 
>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>
>> On 08.06.2017 21:17, Gwendal Roué via swift-evolution wrote:
>>>> Le 8 juin 2017 à 19:40, Brent Royal-Gordon via swift-evolution 
>>>> <swift-evolution at swift.org 
>>>> <mailto:swift-evolution at swift.org><mailto:swift-evolution at swift.org>> a écrit :
>>>>
>>>>> On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution 
>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org> 
>>>>> <mailto:swift-evolution at swift.org>> wrote:
>>>>>
>>>>> Well please no:
>>>>>
>> |let fn2: ((Int, Int)) -> Void = { lhs, rhs in }|
>>>>>
>>>>> Instead use destructuring sugar pitched by Chris Lattner on the other thread:
>>>>>
>>>>> |let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }|
>>>>>
>>>> I think this suggestion is better than the status quo. I'm wondering, though, if 
>>>> we should just drop the outer set of parentheses entirely, unless you're also 
>>>> putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can 
>>>> look like this:
>>>>
>>>> { (x: Int, y: Int) in … }
>>>>
>>>> Or it can look like this:
>>>>
>>>> { x, y in … }
>>>>
>>>> But it *cannot* look like this:
>>>>
>>>> { (x, y) in … }
>>>>
>>>> The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, 
>>>> which immediately destructures the tuple parameter into separate constants.
>>>>
>>>> --
>>>> Brent Royal-Gordon
>>>> Architechies
>>> Hello,
>>> There's a difference, in the mind of people here that try to show how bad were the 
>>> recent changes, between:
>>> 1: closures defined independently
>>> 2: closures given as a parameter to a function.
>>> I think that we all agree that the type of a closure that is defined independently 
>>> should be well defined:
>>> // Choose between (Int, Int) -> () or ((x: Int, y: Int)) -> ()
>>> leta = { (x: Int, y: Int) -> Intin... }
>>> letb = { ((x: Int, y: Int)) -> Intin... }
>>> However, when a closure is given as an argument of a function that expects a 
>>> closure, we ask for the maximum possible flexibility, as Swift 3 did:
>>> funcwantsTwoArguments(_closure: (Int, Int) -> Int) { closure(1, 2) }
>>> wantsTwoArguments{ a, b ina + b }
>>> wantsTwoArguments{ (a, b) ina + b }
>>> wantsTwoArguments{ t int.0+ t.1} // OK, maybe not
>>> funcwantsATupleArgument(_closure: ((Int, Int)) -> Int) { closure((1, 2)) }
>>> wantsATupleArgument{ a, b ina + b }
>>> wantsATupleArgument{ (a, b) ina + b }
>>> wantsATupleArgument{ t int.0+ t.1}
>>> funcwantsANamedTupleArgument(_closure: ((lhs: Int, rhs: Int)) -> Int) { 
>>> closure((lhs: 1, rhs: 2)) }
>>> wantsANamedTupleArgument{ a, b ina + b }
>>> wantsANamedTupleArgument{ (a, b) ina + b }
>>> wantsANamedTupleArgument{ t int.lhs + t.rhs }
>>
>> It's nice to see that we are agreed that func/closures declared separately should 
>> have clearly defined type and (at least for now) can't be interchangeable.
>>
>> And personally I agree that this could be a solution to migration problem, compiler 
>> can generate closure of correct(requested) type if such closure:
>> 1. declared inplace of func call as parameter of that func
>> 2. has no type annotations for its arguments
>> 3. (probably, can discuss) has no parenthesis for its arguments, because one pair 
>> of parenthesis in argument list declares closure of type (list of arguments)->T in 
>> other situations.
>>
>> So, we can have
>>
>> wantsTwoArguments{ a, b in a + b } // (Int,Int)->() will be generated
>> wantsTwoArguments{ (a, b) in a + b } // syntax of (Int,Int)->() closure
>>
>> wantsATupleArgument{ a, b in a + b } // ((Int,Int))->() will be generated
>> wantsATupleArgument{ t in t.0+ t.1 } // syntax of ((Int,Int))->() closure
>>
>> wantsANamedTupleArgument{ a, b in a + b } // ((Int,Int))->() will be generated
>> wantsANamedTupleArgument{ t in t.lhs + t.rhs } // syntax of ((Int,Int))->() closure
> 
> Overloading complicates this. Ignoring for a moment that we cannot declare the 
> following due to https://bugs.swift.org/browse/SR-5129:
> 
> func overloaded(_ fn: (Int, Int) -> Int) { fn(1,2) }
> func overloaded(_ fn: ((Int, Int)) -> Int) { fn((3,4)) }
> overloaded { x, y in x + y }
> overloaded { (x, y) in x + y }
> 

Good point! In this case, as I understand, this solution can't be considered as 
possible and we have(at this moment) only these possible directions:

1. Do not introduce any new syntax/sugar for this migration problem for now. 
Discuss/implement tuple destructuring / closures syntax after Swift 4 release.

2. Introduce new syntax for tuple argument destructuring in closure :
{((x,y)) in ..}
, this looks like good solution but not sure if it is the best and if this can block 
some future improvements here.

3. Introduce new 'let' syntax for tuple argument destructuring in closure :
{ let (x, y) in }
, also the same questions as for (2)

4. Allow type inference for tuple parts in already allowed syntax i.e. :
columns.index { (column: (name, _)) in column.name.lowercased() == lowercaseName }
, this will reduce the problem of migration and will not block any new improvements 
we can have in this area later, as this solution looks just as adding consistency for 
closure syntax: apply the same rule of type annotations but also for tuple parts, not 
just for arguments.
(In case there is no some problems with this proposal)

Do you see any other possible solution ?

> 
> Mark
> 
>>
>> So, { a,b in ...} will be a special syntax for closure which type will be defined 
>> by compiler by type of func's parameter.
>>
>> The question is if community and core team will support this idea, if that idea is 
>> better than other ideas like {((a,b)) in ..} for tuple deconstruction, and if this 
>> could be implemented before Swift 4 release.
>>
>>> *This gives us the ability to deal with unfitted function signatures.* For 
>>> example, most Dictionary methods. Yes, they are usually unfitted:
>>> extensionDictionary{
>>> funcforEach(_body: ((key: Key, value: Value)) throws-> Void) rethrows
>>>     }
>>> Who cares about this named (key:value:) tuple? Absolutely nobody, as exemplified 
>>> by this remarquable Swift 3 snippet below, where no tuple, no `key`, and no 
>>> `value` is in sight:
>>> letscores: [String: Int] = ... // [playerName: score]
>>>     scores.forEach { name, score in
>>> print("\(name): \(score)")
>>>     }
>>> Do you see?
>>
>>    let scores: [String: Int] = ["a":1, "b":2]
>>
>>    scores.forEach { (score: (name:String, value:Int)) in
>>        print("\(score.name): \(score.value)")
>>    }
>>
>> I'm not saying that this syntax as good as in your example, but it is not as 
>> bad/ugly as you say.
>>
>>> Gwendal
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 


More information about the swift-evolution mailing list