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

Vladimir.S svabox at gmail.com
Mon May 30 08:53:44 CDT 2016


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
>


More information about the swift-evolution mailing list