[swift-evolution] [Pitch] Tuple Destructuring in Parameter Lists

Dennis Weissmann dennis at dennisweissmann.me
Mon May 30 10:27:30 CDT 2016


Great catch! I didn’t know that was possible! I’ll add it to the proposal. Thanks!

- Dennis

> On May 30, 2016, at 3:53 PM, Vladimir.S <svabox at gmail.com> wrote:
> 
> I believe you should add currently available syntax to proposal text:
> 
> let c = zip(a,b).reduce(0) { (acc, tuple: (a: Int, b: Int)) in
>  acc + tuple.a + tuple.b
> }
> 
> func takesATuple(tuple : (valueA: Int, valueB: Int)) {
>  print("a: \(tuple.valueA) b:\(tuple.valueB)")
> }
> 
> Not so nice as proposed, but not so ugly as just tuple.0.
> I'm not sure if the proposed feature is adding important improvement to the language.
> 
> On 30.05.2016 0:20, Dennis Weissmann via swift-evolution wrote:
>> 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*
>>  * 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
>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> 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:
>>> 
>>> letstringTuple = [("", "”), ("", "")]
>>> for(i, j) instringTuple {}
>>> 
>>> 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]):
>>> 
>>> letlocalHighestSimilarity = zip(template.points,
>>> resampledPoints).reduce(0.0) { accumulator, points in
>>>  let(template, resampled) = points
>>>  returnaccumulator + 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):
>>> 
>>> funcaccumulateSimilarity(accumulator: Double, for points: (point1:
>>> CGPoint, point2: CGPoint)) -> Double{
>>>  returnaccumulator + 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:
>>> 
>>> funcaccumulateSimilarity(accumulator: Double, for (point1, point2):
>>> (CGPoint, CGPoint)) -> Double{
>>>  returnaccumulator + 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 <geojay at gmail.com
>>>> <mailto:geojay at gmail.com>> wrote:
>>>> 
>>>> Comments below
>>>> 
>>>>> Am 05.05.2016 um 20:22 schrieb Dennis Weissmann via swift-evolution
>>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
>>>>> 
>>>>> Following a short discussion with positive feedback on
>>>>> [swift-users](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
>>>>> 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
>> 
>> 
>> 
>> _______________________________________________
>> 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/20160530/1807434c/attachment.html>


More information about the swift-evolution mailing list