Thanks for everyone participating in this discussion! :)
I’ve drafted a formal proposal, it is available here: 
https://github.com/dennisweissmann/swift-evolution/blob/tuple-destructuring/proposals/0000-tuple-destructuring.md

Please let me know what you think (it would be great if a native speaker could 
take a look at grammar and spelling mistakes). Thanks!
Tuple Destructuring in Parameter Lists
Proposal: SE-NNNN 
<https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
Author(s): Dennis Weissmann <https://github.com/dennisweissmann>
Status: Awaiting review 
<file:///Volumes/Data/Desktop/swift-evolution/proposals/0000-tuple-destructuring.md#rationale>
Review manager: TBD
Introduction
Tuple destructuring is the process of extracting elements from tuples.

This is valid today:

Swift
let point = (x: 20.0, y: 31.0, z: 42.0)
// Approach 1:
let x = point.x
let y = point.y
let z = point.z

// Approach 2:
let (x, y, z) = point

// For-in loops support tuple destructuring
for (x, y, z) in [point] {
  // use x, y, z
}
Swift-evolution thread: [Pitch] Tuple Destructuring in Parameter Lists 
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/16190>
Motivation
This proposal seeks to generalize this behavior for every use case where tuples 
need to be destructured. These are parameter lists in closures and parameter 
lists in functions. Consistency is a major goal of Swift but it is currently 
only possible to destructure tuples in the above mentioned places.

Proposed solution
Extending tuple destructuring to parameter lists seems natural and improves 
consistency in the language.

Closures

Parameters in closures are currently not directly destructable. They can either 
be accessed via .0, .1, etc. or can be destructured by assigning them to 
variables in an explicit statement.

It feels natural to do this right in the parameter list itself (just like with 
for-in loops).

Swift
let a = [0,1,2,3,4,5,6,7,8,9]
let b = [0,1,2,3,4,5,6,7,8,9]

// Allowed today:
let c = zip(a,b).reduce(0) { acc, tuple in
  acc + tuple.0 + tuple.1
}

// Also allowed today:
let c = zip(a,b).reduce(0) { acc, tuple in
  let (valueA, valueB) = tuple
  return acc + valueA + valueB
}

// Proposed syntax:
let c = zip(a,b).reduce(0) { acc, (valueA, valueB) in
  acc + valueA + valueB
}
Functions

When it comes to functions this proposal uses Swift's feature of 
differentiating between internal and external parameter names.

Swift
// Allowed today:
func takesATuple(tuple: (Int, Int)) {
  let valueA = tuple.0
  let valueB = tuple.1
  // ...
}

// Proposed syntax:
func takesATuple(tuple (valueA, valueB): (Int, Int)) {
  // use valueA
  // use valueB
}
This design has no visible effects to the call site of a function but makes it 
very convenient for the function author to use the tuple's elements inside the 
function body.

Impact on existing code
This feature is strictly additive and does not effect current code.

Alternatives considered
Leave it as is destructure in a separate assignment.



- Dennis

> On May 11, 2016, at 10:12 AM, Dennis Weissmann via swift-evolution 
> <[email protected]> wrote:
> 
> Thanks for all your feedback!
> 
> This is the current statistic:
> Closure syntax: All positive
> Function syntax: 3 (or 4) positive, 2 negative
> 
> I’ll try to address the concern Geordie and T.J. have.
> 
>> func takesATuple(someInt: Int, tuple: (valueA: String, valueB: String)) {}
> 
>> It’s true that you still have the ‚overhead‘ of having to type tuple. before 
>> accessing its members. But this is almost always what I want (hopefully 
>> you’d never actually name your tuple ‚tuple‘, instead it’d be a logical 
>> namespace for what it contains). Do you have a real-world example where 
>> you’d need this? To me it seems that in a case like this the API that 
>> produced the tuple would need refining rather than the language itself.
> 
> 
> What you suggest here is not tuple destructuring but using labeled tuples. 
> And while I’m totally with you that this is for many cases the better 
> approach, I still think we should introduce it to functions as well, for 
> consistency and readability reasons.
> In the end inconsistency is what led to this thread because tuple 
> destructuring is already possible today - in for loops:
> 
> let stringTuple = [("", "”), ("", "")]
> for (i, j) in stringTuple {}
> 
> That made me wonder if it’s also possible for closures (because I needed it 
> there - and eventually someone will definitely wonder if it’s possible for 
> function arguments as well).
> 
> You also asked me for my use case. To be honest, I don’t have one for the 
> function version, but imagine the following:
> 
> My current closure use case is this (template.points and resampledPoints are 
> of type [CGPoint]):
> 
> let localHighestSimilarity = zip(template.points, 
> resampledPoints).reduce(0.0) { accumulator, points in
>   let (template, resampled) = points
>   return accumulator + Double(template.x * resampled.x + template.y * 
> resampled.y)
> }
> 
> To reuse this code elsewhere I maybe want to refactor the closure into a 
> function (using your labeled tuple suggestion):
> 
> func accumulateSimilarity(accumulator: Double, for points: (point1: CGPoint, 
> point2: CGPoint)) -> Double {
>   return accumulator + Double(points.point1.x * points.point2.x + 
> points.point1.y * points.point2.y)
> }
> 
> This isn’t particularity readable (image passing a CGRect and you need the 
> points or a more complex calculation). Compare it to this:
> 
> func accumulateSimilarity(accumulator: Double, for (point1, point2): 
> (CGPoint, CGPoint)) -> Double {
>   return accumulator + Double(point1.x * point2.x + point1.y * point2.y)
> }
> 
> You can of course still pass a named tuple instead of an unnamed, but it 
> doesn’t make any difference, which brings me to an aside*.
> 
> I think the second approach makes the calculation much more comprehensible 
> and it just feels “intuitive” (again, at least for me) :).
> 
> 
> - Dennis
> 
> * I’m not sure how scientifically correct the following statement is but 
> strictly speaking (at least for me) (valueA: String, valueB: String) is not 
> of the same type as (String, String) just like func d(string: String, int: 
> Int) is different from func e(_: String, _: Int) though in Swift the tuples 
> are interchangeable (you can pass one where the other is expected).
> 
>> On May 8, 2016, at 6:10 PM, Geordie J <[email protected] 
>> <mailto:[email protected]>> wrote:
>> 
>> Comments below
>> 
>>> Am 05.05.2016 um 20:22 schrieb Dennis Weissmann via swift-evolution 
>>> <[email protected] <mailto:[email protected]>>:
>>> 
>>> Following a short discussion with positive feedback on 
>>> [swift-users](http://thread.gmane.org/gmane.comp.lang.swift.user/1812 
>>> <http://thread.gmane.org/gmane.comp.lang.swift.user/1812>) I’d like to 
>>> discuss the following:
>>> 
>>> Tuples should be destructible into their components in parameter lists.
>>> 
>>> Consider the following code:
>>> 
>>> let a = [0,1,2,3,4,5,6,7,8,9]
>>> let b = [0,1,2,3,4,5,6,7,8,9]
>>> 
>>> let c = zip(a,b).reduce(0) { acc, tuple in
>>>   acc + tuple.0 + tuple.1
>>> }
>>> 
>>> tuple is of type (Int, Int).
>>> 
>>> The problem is that the calculation is not very comprehensible due to .0 
>>> and .1. That’s when destructuring tuples directly in the parameter list 
>>> comes into play:
>>> 
>>> let c = zip(a,b).reduce(0) { acc, (valueA, valueB) in
>>>   acc + valueA + valueB
>>> }
>> 
>> +1 I think this is a great way to go about it.
>> 
>>> 
>>> The above is what I propose should be accepted by the compiler (but 
>>> currently isn’t).
>>> 
>>> Currently tuple destructuring is possible like this:
>>> 
>>> let c = zip(a,b).reduce(0) { (acc, tuple) in
>>>   let (valueA, valueB) = tuple
>>>   return acc + valueA + valueB
>>> }
>>> 
>>> This is not about saving one line ;-). I just find it much more intuitive 
>>> to destructure the tuple in the parameter list itself.
>> 
>> Agreed
>> 
>>> 
>>> The same thing could be done for functions:
>>> 
>>> func takesATuple(someInt: Int, tuple: (String, String))
>>> 
>>> Here we also need to destructure the tuple inside the function, but the 
>>> intuitive place (at least for me) to do this would be the parameter list.
>>> 
>>> In the following example I'm making use of Swift’s feature to name 
>>> parameters different from their labels (for internal use inside the 
>>> function, this is not visible to consumers of the API):
>>> 
>>> func takesATuple(someInt: Int, tuple (valueA, valueB): (String, String))
>> 
>> 
>> I’m not such a fan of this though. I realize what I’m about to write here is 
>> discussing a slightly different point but bear with me: I was under the 
>> impression it was already possible to do something like this (maybe only 
>> possible with typealiases):
>> 
>> func takesATuple(someInt: Int, tuple: (valueA: String, valueB: String)) {}
>> 
>> I find that syntax readable and extensible: you can make a type alias for 
>> your tuple type '(valueA: String, valueB: String)‘, you can then use it like 
>> this:
>> 
>> func takesATuple(someInt: Int, tuple: MyAliasedTupleType) {
>>   print(tuple.valueA)
>> }
>> 
>> It’s true that you still have the ‚overhead‘ of having to type tuple. before 
>> accessing its members. But this is almost always what I want (hopefully 
>> you’d never actually name your tuple ‚tuple‘, instead it’d be a logical 
>> namespace for what it contains). Do you have a real-world example where 
>> you’d need this? To me it seems that in a case like this the API that 
>> produced the tuple would need refining rather than the language itself.
>> 
>>> 
>>> Here valueA and valueB would be directly usable within the function. The 
>>> tuple as a whole would not be available anymore.
>>> 
>>> 
>>> Now it’s your turn!
>>> 
>>> 1. What do you think?
>>> 2. Is this worth being discussed now (i.e. is it implementable in the Swift 
>>> 3 timeframe) or should I delay it?
>>> 
>>> Cheers,
>>> 
>>> - Dennis
>>> _______________________________________________
>>> swift-evolution mailing list
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <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

Reply via email to