[swift-evolution] [Proposal] Disallow implicit conversion between function/closure with a list of parameters and with tuple parameter. Remove function type inconsistency.

Vladimir.S svabox at gmail.com
Sat Jun 25 10:35:54 CDT 2016


I believe this should be done for Swift 3.0 release as this is a *source 
breaking change* and IMO it is very important to remove the inconsistency 
mentioned below.

We removed tuple splatting on caller side and IMO we must complete this job 
to delete the implicit connection between tuple and list of parameters in 
closures/functions.


Currently we have these "features" :
====================================

1. Single tuple as parameter is allowed when list of parameters are required:

let ft1 : (Int,Int) -> Void = { x in print(x.0, x.1)}

(but this causes crash:
let ft2 : (Int,Int) -> Void = { x in print(x) }
)

Opinion: this should not be allowed. Parameter list is required.
`(Int,Int) -> Void` and `((Int,Int)) -> Void` are two different types.


2. Parameter list in closure is allowed when single tuple parameter is 
required:

typealias IntInt = (Int,Int)
typealias IntIntToVoid = (IntInt) -> Void

let tuple : IntInt = (1,2)

func foo(block: IntIntToVoid) { block(tuple) }

foo { x, y in print(x,y)}
foo { (x, y) in print(x, y)}

Opinion: this should not be allowed. Tuple parameter is required.
`((Int,Int)) -> Void` and `(Int,Int) -> Void` are two different types.
Swift should require this syntax to assign tuple parameter's sub-values to 
variables in closure: `{ ((x, y)) in ..}`


3. Inconsistent (and just wrong) function type when a list of parameters 
required(not tuple) :

typealias t1 = (Int, Int) -> Int // clearly here are list of parameters
typealias t2 = ((Int, Int)) -> Int // clearly here is a tuple parameter

print(t1.self) // Prints ((Int, Int)) -> Int  why?
print(t2.self) // Prints ((Int, Int)) -> Int
print(t1.self == t2.self) // true

Opinion: `(Int,Int) -> Void` and `((Int,Int)) -> Void` should be two 
different separate types that can not be implicitly converted to each 
other. Swift's typesystem should separate these types.


4. If the type is the same, why behavior differs :

let add_list:  (Int, Int) -> Int = (+)
let add_tuple: ((Int, Int)) -> Int = (+)

print(add_list.dynamicType == add_tuple.dynamicType) // true

print( add_list(1,2) )
//print( add_list((1,2)) ) // missing argument for parameter #2 in call

//print( add_tuple(1,2) ) // extra argument in call
print( add_tuple((1,2)) )


Proposal:
===============

1. Separate function types with parameter list and a tuple parameter. They 
should be two separate types.

2. Require this syntax to assign tuple parameter's sub-values to variables 
in func/closure: `{ ((x, y)) in ..}`, otherwise (i.e. if `{ (x, y) in ..`) 
treat function/closure as having list of parameters.

3. Disallow implicit conversion between function/closure with a list of 
parameters and function/closure where single tuple is required.
This will stop confusion and make the language consistent how it deal with 
tuples and list of parameters in func/closure.

4. It seems like we should keep the ability to explicitly convert one to 
another as some(many?) code can depend on this current behavior and so we 
need a way to convert old code to new.


More information about the swift-evolution mailing list