[swift-evolution] Revisiting SE-0110

Vladimir.S svabox at gmail.com
Thu Jun 1 15:02:08 CDT 2017


On 01.06.2017 22:46, T.J. Usiyan wrote:
> I, for one, would be willing to accept Xiaodi's suggestion involving `let`–especially 
> if (pipe dream follows) we could use the same syntax in functions/methods to 
> destructure parameters.

Yes, Xiaodi's suggestion also was very attractive. Just to remind:
----------
{ (a, b) -> Int in } // two parameters
{ let (a, b) -> Int in } // destructuring one parameter

{ a, let (b, c) -> Int in } // destructuring two parameters
{ let a, (b, c) -> Int in } // still destructuring two parameters
{ let (a, (b, c)) -> Int in } // destructuring one parameter
{ (a, (b, c)) -> Int in } // error: add 'let' to destructure second parameter
----------

> 
> On Thu, Jun 1, 2017 at 3:32 PM, Vladimir.S via swift-evolution 
> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
>     On 01.06.2017 19:31, Tommaso Piazza wrote:
> 
>         Dear all,
> 
>         I made a comparison of Swift's 4 lack of tuple unsplatting, here is how it
>         stands in comparison with other languages
> 
>         https://gist.github.com/blender/53f9568617654c38a219dd4a8353d935
>         <https://gist.github.com/blender/53f9568617654c38a219dd4a8353d935>
> 
> 
>     Thank you! Very useful information. And also I really like the opinion of
>     @AliSoftware in comments for this article.
> 
>     I'd suggest to add this variant to Swift section in your article:
> 
>     let eighteenOrMore = ["Tom" : 33, "Rebecca" : 17, "Siri" : 5].filter {
>              (arg: (name: String, age: Int)) in arg.age >= 18 }
> 
>     (I believe it is better that 2 others Swift variants.)
> 
>     It seems for me that we need to allow some special syntax for *explicit* tuple
>     destructuring in closures to make all happy.
> 
>     FWIW These suggestions are my favorite:
> 
>     1. Just allow type inference for tuple's destructured variables in this position:
> 
>     .filter { (arg: (name, age)) in arg.age >= 18 }
> 
> 
>     2. (1) + allow underscore for tuple argument name:
> 
>     .filter { (_: (name, age)) in age >= 18 }
> 
> 
>     3. (2) + allow to omit parenthesis (probably only in case of just one tuple argument)
> 
>     .filter { _: (name, age) in age >= 18 }
> 
> 
>     4. Use pattern matching syntax:
> 
>     .filter { case let (name, age) in age >= 18 }
> 
>     (looks similar as allowed today: if case let (name, age) = x { print(name, age) }  )
> 
> 
>     5. Use two pairs of parenthesis :
> 
>     .filter { ((name, age)) in age >= 18 }
> 
>     Btw, about the 5th variant. If took what is allowed today:
>     .filter { (arg: (name: String, age: Int)) in arg.age >= 18 }
>     , and allow type inference for tuple part arguments, we'll have this:
>     .filter { (arg: (name, age)) in arg.age >= 18 }
>     , and if additionally allow skipping of tuple argument declaration we'll have:
>     .filter { ((name, age)) in arg.age >= 18 }
>     I.e. two pairs for parenthesis for tuple destructuring, and such syntax is
>     similar to the type this closure should have : ((String, Int)) -> Bool
> 
> 
> 
> 
> 
>         On Thursday, June 1, 2017 12:25 PM, Vladimir.S via swift-evolution
>         <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
> 
>         On 01.06.2017 0:42, John McCall wrote:
>           >> On May 31, 2017, at 2:02 PM, Stephen Celis <stephen.celis at gmail.com
>         <mailto:stephen.celis at gmail.com> <mailto:stephen.celis at gmail.com
>         <mailto:stephen.celis at gmail.com>>> wrote:
>           >>> On May 28, 2017, at 7:04 PM, John McCall via swift-evolution
>           >>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>         <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>> wrote:
>           >>>
>           >>> Yes, I agree.  We need to add back tuple destructuring in closure parameter
>           >>> lists because this is a serious usability regression.  If we're
>         reluctant to
>           >>> just "do the right thing" to handle the ambiguity of (a,b), we should
>         at least
>           >>> allow it via unambiguous syntax like ((a,b)).  I do think that we
>         should just
>           >>> "do the right thing", however, with my biggest concern being whether
>         there's
>           >>> any reasonable way to achieve that in 4.0.
>           >>
>           >> Closure parameter lists are unfortunately only half of the equation
>         here. This
>           >> change also regresses the usability of point-free expression.
>           >
>           > The consequences for point-free style were expected and cannot really be
>           > eliminated without substantially weakening SE-0110.  Closure convenience
>         seems to
>           > me to be a much more serious regression.
> 
>         John, do you also want to say "and without weakening SE-0066"? Because, if I
>         understand correctly, in this case:
> 
>             func add(_ x: Int, _ y: Int) -> Int {
>               return x + y
>             }
> 
>             zip([1, 2, 3], [4, 5, 6]).map(add)
> 
>         .. we have a clear function type mismatch situation, when map() expects
>         function of
>         type ((Int, Int))->Int, but function of type (Int,Int)->Int is provided ? So
>         probably
>         the additional 'reason' of the 'problem' in this case is SE-0066, no?
>         Or I don't understand the SE-0066 correctly..
>         Do we want to allow implicit conversions between function type
>         ((Int,Int))->Int and
>         (Int,Int)->Int?
> 
>         Quote from SE-0066:
>         ---
>         (Int, Int) -> Int    // function from Int and Int to Int
>         ((Int, Int)) -> Int  // function from tuple (Int, Int) to Int
>         ---
> 
>         During this discussion I see a wish of some group of developers to just
>         return back
>         tuple splatting for function/closure arguments, so they can freely send tuple to
>         function/closure accepting a list of parameters(and probably vise-versa).
>         Is it worth to follow SE-0066 and SE-0110 as is, i.e. disallow tuple
>         deconstructing
>         and then, as additive change improve the situation with tuple
>         splatting/deconstructing later with separate big proposal?
> 
>         Btw, about the SE-0110 proposal. It was discussed, formally reviewed and
>         accepted. I
>         expect that its revision also should be formally proposed/reviewed/accepted to
>         collect a wide range of opinions and thoughts, and attract the attention of
>         developers in this list to the subject.
> 
> 
>         Also, if we revisit SE-0110, will this code be allowed?:
> 
>         func foo(_ callback: ((Int,Int))->Void) {}
>         let mycallback = {(x:Int, y:Int)->Void in }
>         foo(mycallback)
> 
>         and
> 
>         func foo(_ callback: (Int,Int)->Void) {}
>         let mycallback = {(x: (Int, Int))->Void in }
>         foo(mycallback)
> 
>         If so, what will be result of this for both cases? :
> 
>         print(type(of:mycallback)) // (Int,Int)->Void or ((Int,Int))->Void
> 
>         If allowed, do we want to allow implicit conversion between types
>         (Int,Int)->Void and
>         ((Int,Int))->Void in both directions?  (Hello tuple splatting?)
> 
> 
>           >
>           > John.
>           >
>           >
>           >>
>           >> func add(_ x: Int, _ y: Int) -> Int { return x + y }
>           >>
>           >> zip([1, 2, 3], [4, 5, 6]).map(add)
>           >>
>           >> // error: nested tuple parameter '(Int, Int)' of function '(((_.Element,
>           >> _.Element)) throws -> _) throws -> [_]' does not support destructuring
>           >>
>           >> This may not be a common pattern in most projects, but we heavily use
>         this style
>           >> in the Kickstarter app in our functional and FRP code. Definitely not
>         the most
>           >> common coding pattern, but a very expressive one that we rely on.
>           >>
>           >> Our interim solution is a bunch of overloaded helpers, e.g.:
>           >>
>           >> func tupleUp<A, B, C>(_ f: (A, B) -> C) -> ((A, B)) -> C { return }
>           >>
>           >> zip([1, 2, 3], [4, 5, 6]).map(tupleUp(add))
>           >>
>           >> Stephen
>           >
>           > .
>           >
>         _______________________________________________
>         swift-evolution mailing list
>         swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>         <mailto:swift-evolution at swift.org <mailto:swift-evolution at swift.org>>
>         https://lists.swift.org/mailman/listinfo/swift-evolution
>         <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
>     <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> 


More information about the swift-evolution mailing list