[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