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

Mark Lacey mark.lacey at apple.com
Fri Jun 9 01:59:00 CDT 2017


> On Jun 8, 2017, at 11:37 PM, Gwendal Roué via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
>> Le 9 juin 2017 à 07:56, Vladimir.S via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> a écrit :
>> 
>> Yes, we are discussing the *potential* solutions for Swift 4 that can decrease the pain of migration of *some* Swift3 code, given (Int,Int)->() and ((Int,Int))->() are different types in Swift 4.
>> 
>> But, as was said by Mark Lacey in this thread later, there is an "overload" problem for such solution, when closure of kind {x, y in ..} can be sent to overloaded func like here:
>> 
>> 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 }
>> 
>> Compiler is not able to determinate which type of closure do you want in each case. For ((Int,Int))->Int you still need some 'specific' syntax which disambiguate the call. So we *still* need some good syntax to destructure tuple argument in closure to use in place of ((Int,Int))->Int closure.
>> 
>> This means that there is no sense to allow very 'magic' {x,y in ..} syntax and compiler-detected&generated type of closure if we still need good syntax for destructuring tuple argument in ((Int,Int))->().
> 
> Yes, Mark was very right asking about overloads. But is it a problem that does not have a solution which preserves ergonomics?
> 
>     func notOverloaded1(_ closure: (Int, Int) -> Int) -> String { return "notOverloaded1" }
>     func notOverloaded2(_ closure: ((lhs: Int, rhs: Int)) -> Int) -> String { return "notOverloaded3" }
>     
>     func overloaded(_ closure: (Int, Int) -> Int) -> String { return "overloaded 1" }
>     func overloaded(_ closure: ((lhs: Int, rhs: Int)) -> Int) -> String { return "overloaded 2" }
>     
>     // not overloaded => not ambiguous
>     notOverloaded1 { x, y in x + y }
>     notOverloaded1 { (x, y) in x + y }
>     notOverloaded1 { _ in 1 }
>     notOverloaded2 { x, y in x + y }
>     notOverloaded2 { (x, y) in x + y }
>     notOverloaded2 { _ in 1 }
>     
>     // overloaded => resolve ambiguity on closure argument, when possible
>     overloaded { x, y in x + y }        // "overloaded 1"
>     overloaded { (x, y) in x + y }      // "overloaded 1"
>     overloaded { t in t.lhs + t.rhs }   // "overloaded 2"
>     overloaded { (t) in t.lhs + t.rhs } // "overloaded 2”

This is exactly what happens today as a result of SE-0110 since the first two calls take two arguments, and the next two take a single argument.

>     overloaded { _ in 1 }               // error: ambiguous use of ‘overloaded'

With SE-0110 in its current form, this calls the tuple version since there is a single closure parameter listed.

>     overloaded { (_) in 1 }             // "overloaded 1”

With SE-0110 in its current form, this calls the tuple version since there is a single closure parameter listed. I don’t understand why you would suggest this should call the two-argument version since that’s inconsistent with the other closures above that have a single argument.

>     overloaded { (_, _) in 1 }          // "overloaded 2”

With SE-0110 in its current form, this calls the two-argument version since there are two listed arguments.

Mark

> See the error on `overloaded { _ in 1 }`, because _ means "I don't care". Well, here you have to care because of overloading. Ambiguity is resolved with parenthesis. This is a specific behavior for `_`.
> 
> Gwendal
> 
> -------
> 
> PS, I had a little look at how Swift 3 currently behave with overloading ? Badly, actually:
> 
>     // SWIFT 3
>     
>     func f(_ closure: (Int, Int) -> Int) -> String { return "two arguments" }
>     // error: invalid redeclaration of 'f'
>     // func f(_ closure: ((Int, Int)) -> Int) -> String { return "one anonymous tuple argument" }
>     func f(_ closure: ((lhs: Int, rhs: Int)) -> Int) -> String { return "one named tuple argument" }
>     
>     // error: ambiguous use of 'f'
>     // f { x, y in x + y }
>     
>     f { t in t.rhs + t.lhs } // "one named tuple argument"
>     
>     // error: ambiguous use of 'f'
>     // f { t in t.0 + t.1 } // "one named tuple argument"
>     
>     // error: ambiguous use of 'f'
>     let c = { (a: Int, b: Int) -> Int in a + b }
>     type(of: c) // ((Int, Int) -> Int).Type
>     // error: ambiguous use of 'f'
>     // f(c)
> 
> Swift 3 does not allow overloading ((Int, Int)) -> Int and (Int, Int) -> Int, but allows overloading ((lhs: Int, rhs: Int)) -> Int and (Int, Int) -> Int.
> 
> Yet I could never call the (Int, Int) -> Int version, even when I provide with a function that exactly matches its signature. And there are much too many ambiguous situations the compiler can't deal with.
> 
> So yes, there is a problem with Swift 3.
> 
> How is it with Swift 4 (2017-06-02 snapshot)?
> 
>     // SWIFT 4
>     
>     func f(_ closure: (Int, Int) -> Int) -> String { return "two arguments" }
>     // error: invalid redeclaration of 'f'
>     // func f(_ closure: ((Int, Int)) -> Int) -> String { return "one anonymous tuple argument" }
>     func f(_ closure: ((lhs: Int, rhs: Int)) -> Int) -> String { return "one named tuple argument" }
> 
>     f { x, y in x + y }      // "two arguments"
>     f { t in t.rhs + t.lhs } // "one named tuple argument"
>     f { t in t.0 + t.1 }     // "one named tuple argument"
>     
>     let c = { (a: Int, b: Int) -> Int in a + b }
>     type(of: c)              // ((Int, Int) -> Int).Type
>     f(c)                     // "two arguments"
> 
> Much better.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170608/34783fcc/attachment.html>


More information about the swift-evolution mailing list