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

Vladimir.S svabox at gmail.com
Wed Jun 7 10:15:24 CDT 2017

On 07.06.2017 16:20, Gwendal Roué wrote:
>> Le 7 juin 2017 à 15:11, Xiaodi Wu <xiaodi.wu at gmail.com 
>> <mailto:xiaodi.wu at gmail.com>> a écrit :
>> While SE-0025 was generally regarded as unfortunate, the thousands of emails that 
>> followed relitigating it were much, much worse.
>> The removal of implicit tuple splatting, which is *not* SE-0110, was approved on 
>> the understanding that it would be a regression until explicit tuple splatting is 
>> introduced. This tradeoff was considered and approved. It’s clear that you 
>> disagree, but that is not grounds to divert a necessary discussion on mitigating 
>> SE-0110 into relitigating something else.
> Push me out if you want, but will you push out those blatant wounds out as well?
> Example 1
> -        return columns.index { (column, _) in column.lowercased() == lowercaseName }
> +        return columns.index { $0.0.lowercased() == lowercaseName }

Why not
columns.index { (arg: (column: String, _: Int)) in arg.column.lowercased() == 
lowercaseName }

Yes, I understand that first syntax short and not verbose, but the alternative you 
provided IMHO much worse than explicit type declaration in closure.

> Example 2 :
> -            .map { (mappedColumn, baseColumn) -> (Int, String) in
> +            .map { (pair) -> (Int, String) in
> +                let mappedColumn = pair.key
> +                let baseColumn = pair.value

Can't compile something like this even in Swift 3, could you provide a small code 
snippet for this?

> Example 3 :
> -                .map { (table, 
> columns) in "\(table)(\(columns.sorted().joined(separator: ", ")))" }
> +                .map { "\($0.key)(\($0.value.sorted().joined(separator: ", ")))" }

Same, why not

.map { (arg: (table: String, columns: [String])) in 
"\(arg.table)(\(arg.columns.sorted().joined(separator: ", ")))" }

> Example 4 :
> -                dictionary.first { (column, value) in column.lowercased() == 
> orderedColumn.lowercased() }
> +                dictionary.first { $0.key.lowercased() == orderedColumn.lowercased() }


> See also messages from Stephen Cellis, who shows how other kinds of developer code 
> has lost expressivity and clarity with those changes that have been "considered and 
> approved".

Gwendal, no one saying that new syntax is better, that it is good thing that we lost 
the short syntax for tuple argumment deconstructions in closures.

But there is just no easy/obvious way to keep that syntax in Swift 4. The problem 
can't be solved just by not implementing SE-0110, as in Swift4 we should have two 
separate function types: one that takes single tuple argument and second that accepts 
a list of arguments, i.e. (Int,Int)->() and ((Int,Int))->() should be two different 
types now.

This is not just SE-0110, this is also SE-0066, so, to be correct, you should propose 
to revisit it also.

Please look here:

func foo(_ x: Int, _ y: Int) {} // type(of: foo) should be (Int, Int)->()
func bar(_ x (Int, Int)) {} // type(of: bar) should be ((Int, Int))->()

The above is described in SE-0066. Then, you have a closure constants:

var fooClosure = {(x: Int, y: Int) in }
var barClosure = {(x: (Int, Int)) in }

what should be types of these closures? Obvious the same: (Int,Int)->() and 
((Int,Int))->() respectively.

Then you have a func that accepts ((Int,Int))->Int closure:

func schedule(callback: ((Int,Int))->()) {..}

, given type of foo func is (Int, Int)->() , do you suggest to allow sending foo to 
'schedule' func? The same question is for fooClosure

schedule(callback: foo) // ??
schedule(callback: fooClosure) // ??

Probably we can(if technically possible, I don't know) to always allow sending of 
function/closure with list of arguments when function with one tuple is required. I 
don't know how such exceptional rule would looks like inside type system of Swift, 
what should be result of 'foo is ((Int,Int))->()' then and 'type(of:foo) == 
type(of:bar)' in such case.
But this requires a formal proposal, review period and implementation(as I 
understand, better before Swift 4 release). Probably you can submit such proposal, go 
through the review period and help with implementation.
In this case we'll have the same user-friendly closure/function parameters expirience 
but with respect to correct function types.

But currently we have a situation: argument of type ((Int,Int))->() is required, and 
we provide argument of another type : (Int,Int)->() i.e. incorrect type.
The only obvious solution here is using the common rule for type mismatch - disallow 

Currently we have a number of suggestions how we can improve usability for the 
discussed problem:

* use 'let' syntax in closure argument list to deconstruct tuple argument

* use doubled parenthesis to deconstruct tuple argument: { ((key, value)) in .. }

* generation of closure of correct type if closure is declared inside function call 
and arguments have no type annotations, i.e.
   //schedule(callback: foo) // disallowed, type mismatch
   //schedule(callback: fooClosure) // disallowed, type mismatch

   // allowed. compiler will generate closure of type ((Int,Int))->() from this code
   schedule { x,y in }

   // type mismatch, this syntax defines closure of (Int,Int)->() type
   //schedule { (x: Int, y: Int) in }

But because all of this are additional features that can be added later, and each 
required to be reviewed/discussed in details, core team can decide to delay such 
'fix' for after-release period. Let's wait and see what core team had to say about 
this subject.

> Cheers,
> Gwendal

More information about the swift-evolution mailing list