Revisiting SE-0110


#1

Alternatively, for maximum consistency we could make "{ arg in ..." illegal as well, requiring parentheses around "arg". This would mirror the parentheses necessary in e.g., "let f: (Int) -> Int", and there would be no confusion as to why " { arg in ..." is legal but not "{ (key, value) in ...".


(David Sweeris) #2

I think I would support that. Until quite recently, I was under the impression that closures' "inner signatures" were part of that proposal, anyway.

(Come to think of it... were they? I suppose it could be a bug that the "old" syntax is still accepted here.)

- Dave Sweeris

···

On May 26, 2017, at 08:14, Robert Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Alternatively, for maximum consistency we could make "{ arg in ..." illegal as well, requiring parentheses around "arg". This would mirror the parentheses necessary in e.g., "let f: (Int) -> Int", and there would be no confusion as to why " { arg in ..." is legal but not "{ (key, value) in ...".


(Xiaodi Wu) #3

Requiring parentheses in closures like that was discussed during SE-0110
and rejected; the rationale was that it's a useful shorthand, and that the
whole closure syntax is wildly different from function declarations: you
don't need to write the type of the arguments, and you don't need to write
the return value, so why would it make sense to enforce this particular
formality?

At this point, I'd be very much against now requiring it. The whole
rationale for revisiting SE-0110 is that it is breaking lots of code, and
the solution cannot be to break lots of other code. The double parenthesis
syntax or the let syntax for destructuring seem to be fine mitigations, the
former because it strictly parallels the change in syntax in SE-0110 where
a single tuple argument has two sets of parentheses, and the latter because
it's the same destructuring syntax as is used elsewhere for pattern
matching.

···

On Fri, May 26, 2017 at 11:45 David Sweeris via swift-evolution < swift-evolution@swift.org> wrote:

> On May 26, 2017, at 08:14, Robert Bennett via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Alternatively, for maximum consistency we could make "{ arg in ..."
illegal as well, requiring parentheses around "arg". This would mirror the
parentheses necessary in e.g., "let f: (Int) -> Int", and there would be no
confusion as to why " { arg in ..." is legal but not "{ (key, value) in
...".

I think I would support that. Until quite recently, I was under the
impression that closures' "inner signatures" were part of that proposal,
anyway.

(Come to think of it... were they? I suppose it could be a bug that the
"old" syntax is still accepted here.)

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#4

On the contrary I think the solution can absolutely be to break other code. Allowing no parentheses is not a very useful shorthand -- it only saves two characters -- and with SE-0110 actually obscures the logic. Always requiring parentheses makes it clear that "{ (arg) in ..." can be replaced with "{ ((key, value)) in ..." when arg is a 2-tuple; the outer parentheses denote an argument list, and anything inside them is the argument, consistent with other parts of the language. Allowing "{ arg in ..." but not "{ (key, value) in ..." is sure to leave some people scratching their heads because "{ arg in ..." suggests that it is arguments that are passed to closures (as is the case with functions) and not tuples. The correctness of "{ arg in ..." relies too heavily on the equivalence between a 1-tuple and a single element, an equivalence that does not hold for higher arities.

I'm not *too* strongly wed to this, though. I care much more strongly about allowing "{ ((key, value)) in ..." than prohibiting "{ arg in ...". I only brought up the latter to try to improve the consistency of the language and to make clear that "{ ((key, value)) in ..." is the correct way of achieving the old style "{ (key, value) in ..."

···

On May 26, 2017, at 2:57 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Requiring parentheses in closures like that was discussed during SE-0110 and rejected; the rationale was that it's a useful shorthand, and that the whole closure syntax is wildly different from function declarations: you don't need to write the type of the arguments, and you don't need to write the return value, so why would it make sense to enforce this particular formality?

At this point, I'd be very much against now requiring it. The whole rationale for revisiting SE-0110 is that it is breaking lots of code, and the solution cannot be to break lots of other code. The double parenthesis syntax or the let syntax for destructuring seem to be fine mitigations, the former because it strictly parallels the change in syntax in SE-0110 where a single tuple argument has two sets of parentheses, and the latter because it's the same destructuring syntax as is used elsewhere for pattern matching.

On Fri, May 26, 2017 at 11:45 David Sweeris via swift-evolution <swift-evolution@swift.org> wrote:

> On May 26, 2017, at 08:14, Robert Bennett via swift-evolution <swift-evolution@swift.org> wrote:
>
> Alternatively, for maximum consistency we could make "{ arg in ..." illegal as well, requiring parentheses around "arg". This would mirror the parentheses necessary in e.g., "let f: (Int) -> Int", and there would be no confusion as to why " { arg in ..." is legal but not "{ (key, value) in ...".

I think I would support that. Until quite recently, I was under the impression that closures' "inner signatures" were part of that proposal, anyway.

(Come to think of it... were they? I suppose it could be a bug that the "old" syntax is still accepted here.)

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #5

I seem to recall arguing the same with SE-0110, and still hold the same belief; requiring the parenthesis avoids a lot of ambiguity when dealing with tuples, and it's an easy migration to make (Xcode can just add parenthesis to all closures). For code that isn't migrated, syntax errors should crop up naturally anywhere the arguments and closure type no longer match.

I still absolutely believe it's the right call; while closures aren't defined quite the same as functions, that's no reason not to have *some* consistency. There's possibly an argument to make for omitting the brackets on a simple, single parameter closure, but other than that I think it's better to be explicit in all cases.

···

On 26 May 2017, at 20:35, Robert Bennett via swift-evolution <swift-evolution@swift.org> wrote:

On the contrary I think the solution can absolutely be to break other code. Allowing no parentheses is not a very useful shorthand -- it only saves two characters -- and with SE-0110 actually obscures the logic. Always requiring parentheses makes it clear that "{ (arg) in ..." can be replaced with "{ ((key, value)) in ..." when arg is a 2-tuple; the outer parentheses denote an argument list, and anything inside them is the argument, consistent with other parts of the language. Allowing "{ arg in ..." but not "{ (key, value) in ..." is sure to leave some people scratching their heads because "{ arg in ..." suggests that it is arguments that are passed to closures (as is the case with functions) and not tuples. The correctness of "{ arg in ..." relies too heavily on the equivalence between a 1-tuple and a single element, an equivalence that does not hold for higher arities.

I'm not *too* strongly wed to this, though. I care much more strongly about allowing "{ ((key, value)) in ..." than prohibiting "{ arg in ...". I only brought up the latter to try to improve the consistency of the language and to make clear that "{ ((key, value)) in ..." is the correct way of achieving the old style "{ (key, value) in …"


#6

You can break whatever you want.

But whatever the solution you come up with, remember that SE-0110 currently *degrades* the quality of code written by Swift programmers. It can be argued that SE-0110 is a blatant *regression*. Maybe not as bad as the initial introduction of fileprivate, but something which is pretty bad, though.

References:
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036808.html
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036814.html

It would be nice if some responsible language designer would at least *agree* that there is a problem.

Gwendal Roué

···

Le 26 mai 2017 à 21:35, Robert Bennett via swift-evolution <swift-evolution@swift.org> a écrit :

On the contrary I think the solution can absolutely be to break other code.


(Vladimir) #7

Regarding the requiring of parentheses for arguments in closures: I'd suggest to look into SE-0066's paragraph "Should we require parentheses in closure expression parameter lists?" and "Rationale" for this SE: https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000138.html
(SE-0066: https://github.com/apple/swift-evolution/blob/master/proposals/0066-standardize-function-type-syntax.md)

Personally I agree that closures worth their own syntax and that it is important to keep closure's syntax lightweight. So, IMO we should keep {x, y in ..} syntax for closures and this should be jut equivalent for {(x, y) in ..}, i.e. one can use first or second depending on his/her style.

I think the idea of double parentheses for tuple argument destructing in closure is the best solution as it has a relationship to introduced by SE-0066 type of function with tuple argument. I.e. :

func foo(_ x: (Int, Int)) {..}

type(of: foo) // should be ((Int, Int)) -> ()
// note: not (Int, Int) -> ()

var g : ((Int, Int))->() = f

g = {((x1, x2)) in ... } // feels like correct syntax for type ((Int, Int))->()
g = {pair in ... } // also OK, single tuple argument

g = {(x1, x2) in ...} // should be ERROR: incompatible type of closure

But during the discussion of SE-0110 there was some negative reaction for this idea.

Btw, while we are discussing SE-0110 and SE-0066, I'd like to point to another change that can IMO have impact for functional programming, so I believe it is better to discuss this also.
Currently(Swift 3.1) we are allowed to have this:

var f : ()->() = {x in print(x)}
f() // ()
f = {_ in print(x)}

I.e. you can now assign a closure with single argument as function type that has 0 parameters(because of implicit Void argument).

Also, after SE-0066 and 0110 implemented, passing Void to function declared without parameters should not be allowed, so for example you can't call f(print("sdfsdf")) i.e. can't pass result of Void to some other function that is declared without parameters.

Small naive example:

func schedule<T>(with: T, do: (T) -> Void) {...}

//--- can't change this function for some reason ---
func foo() { .. } // just do something
//---

current code:

schedule(with: (), do: foo)

new code:

schedule(with: (), do: {_ in foo()})

Actually I believe this is a good change, but should be taken into consideration.

Vladimir.

···

On 26.05.2017 22:35, Robert Bennett via swift-evolution wrote:

On the contrary I think the solution can absolutely be to break other code. Allowing no parentheses is not a very useful shorthand -- it only saves two characters -- and with SE-0110 actually obscures the logic. Always requiring parentheses makes it clear that "{ (arg) in ..." can be replaced with "{ ((key, value)) in ..." when arg is a 2-tuple; the outer parentheses denote an argument list, and anything inside them is the argument, consistent with other parts of the language. Allowing "{ arg in ..." but not "{ (key, value) in ..." is sure to leave some people scratching their heads because"{ arg in ..." suggests that it is arguments that are passed to closures (as is the case with functions) and not tuples. The correctness of "{ arg in ..." relies too heavily on the equivalence between a 1-tuple and a single element, an equivalence that does not hold for higher arities.

I'm not *too* strongly wed to this, though. I care much more strongly about allowing "{ ((key, value)) in ..." than prohibiting "{ arg in ...". I only brought up the latter to try to improve the consistency of the language and to make clear that "{ ((key, value)) in ..." is the correct way of achieving the old style "{ (key, value) in ..."

On May 26, 2017, at 2:57 PM, Xiaodi Wu <xiaodi.wu@gmail.com > <mailto:xiaodi.wu@gmail.com>> wrote:

Requiring parentheses in closures like that was discussed during SE-0110 and rejected; the rationale was that it's a useful shorthand, and that the whole closure syntax is wildly different from function declarations: you don't need to write the type of the arguments, and you don't need to write the return value, so why would it make sense to enforce this particular formality?

At this point, I'd be very much against now requiring it. The whole rationale for revisiting SE-0110 is that it is breaking lots of code, and the solution cannot be to break lots of other code. The double parenthesis syntax or the let syntax for destructuring seem to be fine mitigations, the former because it strictly parallels the change in syntax in SE-0110 where a single tuple argument has two sets of parentheses, and the latter because it's the same destructuring syntax as is used elsewhere for pattern matching.
On Fri, May 26, 2017 at 11:45 David Sweeris via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    > On May 26, 2017, at 08:14, Robert Bennett via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
    >
    > Alternatively, for maximum consistency we could make "{ arg in ..." illegal
    as well, requiring parentheses around "arg". This would mirror the parentheses
    necessary in e.g., "let f: (Int) -> Int", and there would be no confusion as to
    why " { arg in ..." is legal but not "{ (key, value) in ...".

    I think I would support that. Until quite recently, I was under the impression
    that closures' "inner signatures" were part of that proposal, anyway.

    (Come to think of it... were they? I suppose it could be a bug that the "old"
    syntax is still accepted here.)

    - Dave Sweeris
    _______________________________________________
    swift-evolution mailing list
    swift-evolution@swift.org <mailto:swift-evolution@swift.org>
    https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#8

One more link, about the loss of single-lign closures induced by SE-0110: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036792.html

Gwendal

···

Le 27 mai 2017 à 14:20, Gwendal Roué <gwendal.roue@gmail.com> a écrit :

Le 26 mai 2017 à 21:35, Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On the contrary I think the solution can absolutely be to break other code.

You can break whatever you want.

But whatever the solution you come up with, remember that SE-0110 currently *degrades* the quality of code written by Swift programmers. It can be argued that SE-0110 is a blatant *regression*. Maybe not as bad as the initial introduction of fileprivate, but something which is pretty bad, though.

References:
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036808.html
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036814.html


(John McCall) #9

Regarding the requiring of parentheses for arguments in closures: I'd suggest to look into SE-0066's paragraph "Should we require parentheses in closure expression parameter lists?" and "Rationale" for this SE: https://lists.swift.org/pipermail/swift-evolution-announce/2016-May/000138.html
(SE-0066: https://github.com/apple/swift-evolution/blob/master/proposals/0066-standardize-function-type-syntax.md)

Personally I agree that closures worth their own syntax and that it is important to keep closure's syntax lightweight. So, IMO we should keep {x, y in ..} syntax for closures and this should be jut equivalent for {(x, y) in ..}, i.e. one can use first or second depending on his/her style.

I think the idea of double parentheses for tuple argument destructing in closure is the best solution as it has a relationship to introduced by SE-0066 type of function with tuple argument. I.e. :

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.

John.

···

On May 27, 2017, at 8:01 AM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

func foo(_ x: (Int, Int)) {..}

type(of: foo) // should be ((Int, Int)) -> ()
// note: not (Int, Int) -> ()

var g : ((Int, Int))->() = f

g = {((x1, x2)) in ... } // feels like correct syntax for type ((Int, Int))->()
g = {pair in ... } // also OK, single tuple argument

g = {(x1, x2) in ...} // should be ERROR: incompatible type of closure

But during the discussion of SE-0110 there was some negative reaction for this idea.

Btw, while we are discussing SE-0110 and SE-0066, I'd like to point to another change that can IMO have impact for functional programming, so I believe it is better to discuss this also.
Currently(Swift 3.1) we are allowed to have this:

var f : ()->() = {x in print(x)}
f() // ()
f = {_ in print(x)}

I.e. you can now assign a closure with single argument as function type that has 0 parameters(because of implicit Void argument).

Also, after SE-0066 and 0110 implemented, passing Void to function declared without parameters should not be allowed, so for example you can't call f(print("sdfsdf")) i.e. can't pass result of Void to some other function that is declared without parameters.

Small naive example:

func schedule<T>(with: T, do: (T) -> Void) {...}

//--- can't change this function for some reason ---
func foo() { .. } // just do something
//---

current code:

schedule(with: (), do: foo)

new code:

schedule(with: (), do: {_ in foo()})

Actually I believe this is a good change, but should be taken into consideration.

Vladimir.

On 26.05.2017 22:35, Robert Bennett via swift-evolution wrote:

On the contrary I think the solution can absolutely be to break other code. Allowing no parentheses is not a very useful shorthand -- it only saves two characters -- and with SE-0110 actually obscures the logic. Always requiring parentheses makes it clear that "{ (arg) in ..." can be replaced with "{ ((key, value)) in ..." when arg is a 2-tuple; the outer parentheses denote an argument list, and anything inside them is the argument, consistent with other parts of the language. Allowing "{ arg in ..." but not "{ (key, value) in ..." is sure to leave some people scratching their heads because"{ arg in ..." suggests that it is arguments that are passed to closures (as is the case with functions) and not tuples. The correctness of "{ arg in ..." relies too heavily on the equivalence between a 1-tuple and a single element, an equivalence that does not hold for higher arities.
I'm not *too* strongly wed to this, though. I care much more strongly about allowing "{ ((key, value)) in ..." than prohibiting "{ arg in ...". I only brought up the latter to try to improve the consistency of the language and to make clear that "{ ((key, value)) in ..." is the correct way of achieving the old style "{ (key, value) in ..."
On May 26, 2017, at 2:57 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Requiring parentheses in closures like that was discussed during SE-0110 and rejected; the rationale was that it's a useful shorthand, and that the whole closure syntax is wildly different from function declarations: you don't need to write the type of the arguments, and you don't need to write the return value, so why would it make sense to enforce this particular formality?

At this point, I'd be very much against now requiring it. The whole rationale for revisiting SE-0110 is that it is breaking lots of code, and the solution cannot be to break lots of other code. The double parenthesis syntax or the let syntax for destructuring seem to be fine mitigations, the former because it strictly parallels the change in syntax in SE-0110 where a single tuple argument has two sets of parentheses, and the latter because it's the same destructuring syntax as is used elsewhere for pattern matching.
On Fri, May 26, 2017 at 11:45 David Sweeris via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

   > On May 26, 2017, at 08:14, Robert Bennett via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
   >
   > Alternatively, for maximum consistency we could make "{ arg in ..." illegal
   as well, requiring parentheses around "arg". This would mirror the parentheses
   necessary in e.g., "let f: (Int) -> Int", and there would be no confusion as to
   why " { arg in ..." is legal but not "{ (key, value) in ...".

   I think I would support that. Until quite recently, I was under the impression
   that closures' "inner signatures" were part of that proposal, anyway.

   (Come to think of it... were they? I suppose it could be a bug that the "old"
   syntax is still accepted here.)

   - Dave Sweeris
   _______________________________________________
   swift-evolution mailing list
   swift-evolution@swift.org <mailto:swift-evolution@swift.org>
   https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#10

Er, I think I misunderstood the proposal and discussion and prior work (e.g. SE-0066 <https://github.com/apple/swift-evolution/blob/master/proposals/0066-standardize-function-type-syntax.md>) and so my contribution to this discussion was not great. Here are my revised thoughts in light of a reread of the relevant material. Below is an outline of what I think closure signatures should look like. (For all of the below examples, the ability to explicitly include return type in the closure "preamble" is assumed.)

0) If a closure takes no arguments, acceptable "preambles" should be: omitting named variables and the "in" keyword, "() in", or "Void in".

1) If a closure takes one argument, acceptable preambles should be: "(arg) in" or "(_) in".

2) If a closure takes at least two arguments, acceptable preambles should be, e.g., "(x, y) in", with every spot in the tuple replaceable by an underscore.

3) If a closure takes at least one argument, "_ in" means that *all* arguments are ignored, regardless of how many there are. For a single-argument closure, "_ in" is shorthand for "(_) in". For multiple-argument closures, it is shorthand for "(_, _, …, _) in". "_ in" is not allowed for nullary closures.

4) Tuple unpacking can be achieved by replacing the variables inside the outer parentheses with tuples. For a Dictionary, whose Element is of type (key: Key, value: Value), this means writing "((key, value)) in", i.e., replacing the "arg" in "(arg) in" with "(key, value)". For a Dictionary<String, (Int, Double)>, you could do "((s, (i, d))) in". If and only if the tuple in question had named positions, they would also be allowed in the closure preamble. So for a Dictionary<String, (length: Int, score: Double)> you could write "((key: s, (length: i, score: d))) in". The rules for tuple position labels would be the same as for regular tuples (whatever those may be; I can never keep track of exactly what is (dis)allowed).

5) The only point I foresee sparking much debate: there should be no permissible shorthands/syntactic sugar except for those outlined above. In particular, parentheses should be required for all closures taking a nonzero number of arguments.

Rationale: Allowing the shorthand "arg in" suggests that when arg is a tuple, "(x, y) in" should accomplish the unpacking, which is incorrect. Allowing the shorthand "x, y in" suggests that for single-argument closures, "arg in" should be allowed, which (according to this) would be incorrect. I don't like the idea of special-casing the syntactic sugar and allowing users to drop the outer parentheses as long as the closure does not take one argument that is a tuple (I think that's the only case the compiler would be unable to resolve). Also, I would imagine the migration path to enforce parentheses around arguments is trivial to implement.

Requiring parentheses also finishes the job started by SE-0066. In fact it more or less perfectly mirrors the proposed solution <https://github.com/apple/swift-evolution/blob/master/proposals/0066-standardize-function-type-syntax.md#proposed-solution> to the problem of function types.. The only difference here is that we're talking about argument lists, not function types, but why pass up an opportunity to unify the two?

···


Robert Bennett

On May 27, 2017, at 8:23 AM, Gwendal Roué <gwendal.roue@gmail.com> wrote:

Le 27 mai 2017 à 14:20, Gwendal Roué <gwendal.roue@gmail.com <mailto:gwendal.roue@gmail.com>> a écrit :

Le 26 mai 2017 à 21:35, Robert Bennett via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

On the contrary I think the solution can absolutely be to break other code.

You can break whatever you want.

But whatever the solution you come up with, remember that SE-0110 currently *degrades* the quality of code written by Swift programmers. It can be argued that SE-0110 is a blatant *regression*. Maybe not as bad as the initial introduction of fileprivate, but something which is pretty bad, though.

References:
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036808.html
- https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036814.html

One more link, about the loss of single-lign closures induced by SE-0110: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170522/036792.html

Gwendal


#11

I agree with John, the best thing for actually using Swift is to allow
tuple destructuring here, and the outermost parentheses (around the entire
parameter list of a closure) should continue to be optional. Requiring
double-parens would seem unnecessarily and arbitrarily…whatever the
opposite of “convenient” is.

Nevin

···

On Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution < swift-evolution@swift.org> wrote:

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.

John.


#12

Closure parameter lists are unfortunately only half of the equation here. This change also regresses the usability of point-free expression.

   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

···

On May 28, 2017, at 7:04 PM, John McCall via swift-evolution <swift-evolution@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.


(Vladimir) #13

    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.

    John.

I agree with John, the best thing for actually using Swift is to allow tuple destructuring here, and the outermost parentheses (around the entire parameter list of a closure) should continue to be optional. Requiring double-parens would seem unnecessarily and arbitrarily…whatever the opposite of “convenient” is.

Nevin

If I understand correctly, correct me if I'm wrong, after *full* implementation of SE-0066, the function with two parameters should have different type than function with one tuple parameter:

I.e.
typealias FuncParams = (Int, Int)->()
typealias FuncTuple = ((Int, Int))->()

print(FuncParams.self) // should be (Int, Int)->()
print(FuncTuple.self) // should be ((Int, Int))->()

So, if we have

func barParams(_ f: FuncParams) {
  f(1,2)
}

func barTuple(_ f: FuncTuple) {
  f((1,2))
}

and

func fooWithParams(_ x: Int, _ y: Int) { }
func fooWithTuple(_ tuple: (Int, Int)) { }

.. we should not be able to call

barParams(fooWithTuple) // should be error: incompatible types
barTuple(fooWithParams) // should be error: incompatible types

If so, are you suggesting that this code should still be valid?

barParams({tuple in}) // ?
barTuple({x,y in}) // ?

Will {x,y in} closure has ((Int, Int))->() type in this case?
And if {tuple in} should be of type (Int,Int)->() ?

If I'm not missing something, the only reasonable solution here is follow the SE-0066 rules for function types and allow special syntax ((x,y)) to deconstruct tuple parts in closure argument list, and such closure will have type ((Int,Int))->() as expected. So we'll have:

barTuple({((x,y)) in ... })

···

On 29.05.2017 18:26, Nevin Brackett-Rozinsky via swift-evolution wrote:

On Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(John McCall) #14

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.

···

On May 31, 2017, at 2:02 PM, Stephen Celis <stephen.celis@gmail.com> wrote:

On May 28, 2017, at 7:04 PM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

  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


#15

Seeing this makes me rethink the severity of this problem. I wonder if it would be worth it to add a starMap function that works like map, but unpacks tuples into separate arguments. So dict.map { (key, value) in ... } is still illegal but dict.starMap { (key, value) in ... } works like dict.map used to.

···

On May 31, 2017, at 5:02 PM, Stephen Celis <stephen.celis@gmail.com> wrote:

On May 28, 2017, at 7:04 PM, John McCall via swift-evolution <swift-evolution@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.

  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


(Vladimir) #16

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:

···

On 01.06.2017 0:42, John McCall wrote:

On May 31, 2017, at 2:02 PM, Stephen Celis <stephen.celis@gmail.com> wrote:

On May 28, 2017, at 7:04 PM, John McCall via swift-evolution >>> <swift-evolution@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.

---
(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

.


#17

The word you're looking for is inconvenient :grin:

If we allow dropping parentheses for tuples then we should disallow the outer parentheses to avoid ambiguity between multi-argument closures and closures taking a single tuple as their argument. Then, a closure taking multiple arguments would have to be written as "x, y in" and a closure taking a tuple as "(x, y) in". I'm ok with this too. I just want to make sure whatever solution we come up with adheres to the goal of SE-0110, which is to clearly separate functions taking multiple arguments from functions taking a tuple. For clarity, whatever solution we come up with should wrap the arguments within a tuple in an extra pair of parentheses. So for a multi-argument and single-tuple closure, respectively, either "x, y in" and "(x, y) in", or "(x, y) in" and "((x, y)) in".

···

On May 29, 2017, at 1:32 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 29.05.2017 18:26, Nevin Brackett-Rozinsky via swift-evolution wrote:
On Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
   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.
   John.
I agree with John, the best thing for actually using Swift is to allow tuple destructuring here, and the outermost parentheses (around the entire parameter list of a closure) should continue to be optional. Requiring double-parens would seem unnecessarily and arbitrarily…whatever the opposite of “convenient” is.
Nevin

If I understand correctly, correct me if I'm wrong, after *full* implementation of SE-0066, the function with two parameters should have different type than function with one tuple parameter:

I.e.
typealias FuncParams = (Int, Int)->()
typealias FuncTuple = ((Int, Int))->()

print(FuncParams.self) // should be (Int, Int)->()
print(FuncTuple.self) // should be ((Int, Int))->()

So, if we have

func barParams(_ f: FuncParams) {
   f(1,2)
}

func barTuple(_ f: FuncTuple) {
   f((1,2))
}

and

func fooWithParams(_ x: Int, _ y: Int) { }
func fooWithTuple(_ tuple: (Int, Int)) { }

.. we should not be able to call

barParams(fooWithTuple) // should be error: incompatible types
barTuple(fooWithParams) // should be error: incompatible types

If so, are you suggesting that this code should still be valid?

barParams({tuple in}) // ?
barTuple({x,y in}) // ?

Will {x,y in} closure has ((Int, Int))->() type in this case?
And if {tuple in} should be of type (Int,Int)->() ?

If I'm not missing something, the only reasonable solution here is follow the SE-0066 rules for function types and allow special syntax ((x,y)) to deconstruct tuple parts in closure argument list, and such closure will have type ((Int,Int))->() as expected. So we'll have:

barTuple({((x,y)) in ... })

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#18

Vladimir, using your function definitions I would like to have:

// Two parameters:
barParams{ t in } // invalid, too few parameters
barParams{ (t) in } // invalid, too few parameters
barParams{ x, y in } // valid, two parameters
barParams{ (x, y) in } // valid, two parameters with optional parentheses
barParams{ ((x, y)) in }// invalid, too few parameters

// One parameter which is a 2-tuple:
barTuple{ t in } // valid, one tuple parameter
barTuple{ (t) in } // valid, one tuple parameter with optional
parentheses
barTuple{ x, y in } // invalid, too many parameters
barTuple{ (x, y) in } // valid, destructuring one tuple
barTuple{ ((x, y)) in } // valid, destructuring one tuple with optional
parentheses

For completeness: if a closure takes *any number* of parameters, it should
be legal to list one identifier per parameter, separated by commas, with
optionally a pair of parentheses surrounding that list. Furthermore, if
*any* parameter is a tuple, it should be possible to replace the identifier
corresponding to that parameter with a pair of parentheses containing a
number of identifiers (separated by commas) equal to the arity of the
tuple. And this tuple-destructuring should work recursively, so if a tuple
contains a tuple [contains a tuple…] the inner tuples may optionally be
destructured as well.

To be clear: each tuple being destructured must have a pair of parentheses
containing the identifiers it is being destructured into. It is only the
very outermost parentheses around the entire parameter list of the closure
which should be optional.

Nevin

···

On Mon, May 29, 2017 at 1:32 PM, Vladimir.S <svabox@gmail.com> wrote:

On 29.05.2017 18:26, Nevin Brackett-Rozinsky via swift-evolution wrote:

On Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution < >> swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

    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.

    John.

I agree with John, the best thing for actually using Swift is to allow
tuple destructuring here, and the outermost parentheses (around the entire
parameter list of a closure) should continue to be optional. Requiring
double-parens would seem unnecessarily and arbitrarily…whatever the
opposite of “convenient” is.

Nevin

If I understand correctly, correct me if I'm wrong, after *full*
implementation of SE-0066, the function with two parameters should have
different type than function with one tuple parameter:

I.e.
typealias FuncParams = (Int, Int)->()
typealias FuncTuple = ((Int, Int))->()

print(FuncParams.self) // should be (Int, Int)->()
print(FuncTuple.self) // should be ((Int, Int))->()

So, if we have

func barParams(_ f: FuncParams) {
        f(1,2)
}

func barTuple(_ f: FuncTuple) {
        f((1,2))
}

and

func fooWithParams(_ x: Int, _ y: Int) { }
func fooWithTuple(_ tuple: (Int, Int)) { }

.. we should not be able to call

barParams(fooWithTuple) // should be error: incompatible types
barTuple(fooWithParams) // should be error: incompatible types

If so, are you suggesting that this code should still be valid?

barParams({tuple in}) // ?
barTuple({x,y in}) // ?

Will {x,y in} closure has ((Int, Int))->() type in this case?
And if {tuple in} should be of type (Int,Int)->() ?

If I'm not missing something, the only reasonable solution here is follow
the SE-0066 rules for function types and allow special syntax ((x,y)) to
deconstruct tuple parts in closure argument list, and such closure will
have type ((Int,Int))->() as expected. So we'll have:

barTuple({((x,y)) in ... })

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #19

The word you're looking for is inconvenient :grin:

If we allow dropping parentheses for tuples then we should disallow the outer
parentheses to avoid ambiguity between multi-argument closures and closures taking
a single tuple as their argument. Then, a closure taking multiple arguments would
have to be written as "x, y in" and a closure taking a tuple as "(x, y) in". I'm
ok with this too. I just want to make sure whatever solution we come up with
adheres to the goal of SE-0110, which is to clearly separate functions taking
multiple arguments from functions taking a tuple. For clarity, whatever solution
we come up with should wrap the arguments within a tuple in an extra pair of
parentheses. So for a multi-argument and single-tuple closure, respectively,
either "x, y in" and "(x, y) in", or "(x, y) in" and "((x, y)) in".

Well, from my point of view closure deserves some special syntax and rules. And IMO
it is important to support currently allowed syntax of {x,y in} == {(x,y) in ..} to reduce the code breaking changes and to keep simple clear syntax in most situations.

But we should resolve the ambiguity related to tuple splatting/deconstructing in arguments of closure. Two ways: disallow this feature or introduce separate syntax.

So, the only *correct* way I see - is use of ((x,y)) for deconstructing tuple argument.

In this case, when close with single tuple argument(say of type (Int,Int)) is required, we can write this:
{tuple in ...}
{(tuple) in ...}
{((x,y)) in ...} // tuple deconstruction into x and y variables
//{(x,y) in ...} // should be disallowed

the ((x,y)) syntax follows the way of SE-0066's type of function with single tuple argument:"...
(Int, Int) -> Int // function from Int and Int to Int
((Int, Int)) -> Int // function from tuple (Int, Int) to Int
..."

Again, please note that SE-0110 is based on previous accepted proposals: SE-0066 and SE-0029.

···

On 29.05.2017 21:07, Robert Bennett wrote:

On May 29, 2017, at 1:32 PM, Vladimir.S via swift-evolution >> <swift-evolution@swift.org> wrote:

On 29.05.2017 18:26, Nevin Brackett-Rozinsky via swift-evolution wrote: On
Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution
<swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: 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. John. I agree with John, the best thing for actually
using Swift is to allow tuple destructuring here, and the outermost
parentheses (around the entire parameter list of a closure) should continue to
be optional. Requiring double-parens would seem unnecessarily and
arbitrarily…whatever the opposite of “convenient” is. Nevin

If I understand correctly, correct me if I'm wrong, after *full* implementation
of SE-0066, the function with two parameters should have different type than
function with one tuple parameter:

I.e. typealias FuncParams = (Int, Int)->() typealias FuncTuple = ((Int,
Int))->()

print(FuncParams.self) // should be (Int, Int)->() print(FuncTuple.self) //
should be ((Int, Int))->()

So, if we have

func barParams(_ f: FuncParams) { f(1,2) }

func barTuple(_ f: FuncTuple) { f((1,2)) }

and

func fooWithParams(_ x: Int, _ y: Int) { } func fooWithTuple(_ tuple: (Int,
Int)) { }

.. we should not be able to call

barParams(fooWithTuple) // should be error: incompatible types barTuple(fooWithParams) // should be error: incompatible types

If so, are you suggesting that this code should still be valid?

barParams({tuple in}) // ? barTuple({x,y in}) // ?

Will {x,y in} closure has ((Int, Int))->() type in this case? And if {tuple in}
should be of type (Int,Int)->() ?

If I'm not missing something, the only reasonable solution here is follow the
SE-0066 rules for function types and allow special syntax ((x,y)) to deconstruct
tuple parts in closure argument list, and such closure will have type
((Int,Int))->() as expected. So we'll have:

barTuple({((x,y)) in ... })

_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #20

Vladimir, using your function definitions I would like to have:

// Two parameters:
barParams{ t in } // invalid, too few parameters
barParams{ (t) in } // invalid, too few parameters
barParams{ x, y in } // valid, two parameters
barParams{ (x, y) in } // valid, two parameters with optional parentheses
barParams{ ((x, y)) in }// invalid, too few parameters

OK, the same as I suggest.

// One parameter which is a 2-tuple:
barTuple{ t in } // valid, one tuple parameter
barTuple{ (t) in } // valid, one tuple parameter with optional parentheses
barTuple{ x, y in } // invalid, too many parameters

OK..

barTuple{ (x, y) in } // valid, destructuring one tuple
barTuple{ ((x, y)) in } // valid, destructuring one tuple with optional parentheses

Here is the problem for me. According to SE-0066 closure declared as {(x,y) in} should be of type (Int,Int)->() and not ((Int,Int))->().
Why do you want to be able to pass wrong function/closure type into barTuple?

For completeness: if a closure takes *any number* of parameters, it should be legal to list one identifier per parameter, separated by commas, with optionally a pair of parentheses surrounding that list. Furthermore, if *any* parameter is a tuple, it should be possible to replace the identifier corresponding to that parameter with a pair of parentheses containing a number of identifiers (separated by commas) equal to the arity of the tuple. And this tuple-destructuring should work recursively, so if a tuple contains a tuple [contains a tuple…] the inner tuples may optionally be destructured as well.

Well.. Theoretically I don't see any problem with this in suggested "double pair of parentheses" syntax:

typealias MyTuple = (Int, (String, (Double, Bool)))

func doSomething(callback: ((Int, Int), MyTuple)->()) {}

doSomething {point, mytuple in }
doSomething {((x,y)), mytuple in }
doSomething {((x,y)), ((i, sdb)) in }
doSomething {point, ((i, ((s, ((d, b)))))) in }

//probably even just that flat syntax if all values are destructured
doSomething {((x, y)), ((i, s, d, b)) in }

doSomething {(t, myt) in } // here we see that there are 2 arguments, not tuples
doSomething {(((x,y)), myt) in }
doSomething {(((x,y)), ((i, tuple))) in }
doSomething {(point, ((i, ((s, ((d, b))))))) in }

the double parentheses in closure argument declaration clearly shows that this is a tuple deconstruction.

The question if we'll decide to allow such feature(extended ability for tuple deconstructing in closure arguments).

To be clear: each tuple being destructured must have a pair of parentheses containing the identifiers it is being destructured into. It is only the very outermost parentheses around the entire parameter list of the closure which should be optional.

I see your position, but as I said, IMO single pair of parentheses is very ambiguous if it is a list of arguments or tuple deconstruction and we shouldn't disallow single pair of parentheses to surround a simple list of closure arguments in declaration.

···

On 29.05.2017 21:08, Nevin Brackett-Rozinsky wrote:

Nevin

On Mon, May 29, 2017 at 1:32 PM, Vladimir.S <svabox@gmail.com > <mailto:svabox@gmail.com>> wrote:

    On 29.05.2017 18:26, Nevin Brackett-Rozinsky via swift-evolution wrote:

        On Sun, May 28, 2017 at 7:04 PM, John McCall via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org> > <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:

             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.

             John.

        I agree with John, the best thing for actually using Swift is to allow tuple
        destructuring here, and the outermost parentheses (around the entire
        parameter list of a closure) should continue to be optional. Requiring
        double-parens would seem unnecessarily and arbitrarily…whatever the opposite
        of “convenient” is.

        Nevin

    If I understand correctly, correct me if I'm wrong, after *full* implementation
    of SE-0066, the function with two parameters should have different type than
    function with one tuple parameter:

    I.e.
    typealias FuncParams = (Int, Int)->()
    typealias FuncTuple = ((Int, Int))->()

    print(FuncParams.self) // should be (Int, Int)->()
    print(FuncTuple.self) // should be ((Int, Int))->()

    So, if we have

    func barParams(_ f: FuncParams) {
             f(1,2)
    }

    func barTuple(_ f: FuncTuple) {
             f((1,2))
    }

    and

    func fooWithParams(_ x: Int, _ y: Int) { }
    func fooWithTuple(_ tuple: (Int, Int)) { }

    .. we should not be able to call

    barParams(fooWithTuple) // should be error: incompatible types
    barTuple(fooWithParams) // should be error: incompatible types

    If so, are you suggesting that this code should still be valid?

    barParams({tuple in}) // ?
    barTuple({x,y in}) // ?

    Will {x,y in} closure has ((Int, Int))->() type in this case?
    And if {tuple in} should be of type (Int,Int)->() ?

    If I'm not missing something, the only reasonable solution here is follow the
    SE-0066 rules for function types and allow special syntax ((x,y)) to deconstruct
    tuple parts in closure argument list, and such closure will have type
    ((Int,Int))->() as expected. So we'll have:

    barTuple({((x,y)) in ... })

        _______________________________________________
        swift-evolution mailing list
        swift-evolution@swift.org <mailto:swift-evolution@swift.org>
        https://lists.swift.org/mailman/listinfo/swift-evolution
        <https://lists.swift.org/mailman/listinfo/swift-evolution>