Proposal: Always flatten the single element tuple

>
> This is not what was meant during discussion about re-evaluating
SE-0110. Tuples already behave as described, but function argument lists
are not tuples and have not been for a very long time: see SE-0029, SE-0066.
>
> Also, consider SE-0046, which makes possible labels in single-argument
argument lists (not possible in tuples), and SE-0060, which prohibits
arbitrary reordering (although still possible in tuples). This is to say
that the whole direction of Swift since version 2 has been to erase the
historical relationship between tuples and argument lists.
>
> The question is how to accommodate some common use cases for
destructuring as a matter of syntactic sugar after having carefully
distinguished argument lists and tuples in the compiler, which is a given,
not how to roll back a change that was settled in 2011, by Chris’s telling.

As one of many who is directly affected and wants resolution, I think the
discussion of re-evaluating SE-0110 most certainly expands to these earlier
decisions. While Swift has moved in the direction of distinguishing tuples
and argument lists in decidedly different ways, I think it's perfectly
valid to consider the alternative: a simpler and equally-valid solution:
flattened tuples that are isomorphic to argument lists.

This is an issue that’s coming up on this list with alarming frequency:
meta-debates on what is even in scope for consideration. Reversing five or
six proposals as well as a decision taken prior to Swift 2 is certainly not
“perfectly valid.” Simply, the ship has long sailed.

Argument labels certainly complicate semantics, but we still erase tuple

labels in general use to this day. Meanwhile, prohibited arbitrary
reordering remains complicated (I just recently worked with an SPM module
that advertised instructions the compiler disallowed, and it took awhile to
find the correct, compiler-allowed ordering while troubleshooting
confusing, compiler-driven error messaging).

Those are enums, which were aligned with argument lists instead of tuples
in yet another proposal, SE-0155.

All of these hiccups are the result of compiler optimizations and

simplifications that hinder end-user ergonomics.

Having the compiler perform reliably is the most basic of user ergonomics,
wouldn’t you say?

Again: while I can appreciate making the compiler as simple and strict as

···

On Wed, Jun 7, 2017 at 00:39 Stephen Celis <stephen.celis@gmail.com> wrote:

> On Jun 7, 2017, at 1:05 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
possible, the end-user and end-use cases suffer along the way.

Stephen

Those are enums, which were aligned with argument lists instead of tuples in yet another proposal, SE-0155.

Can you clarify? I wasn't referring to enums, and SE-0155 starts by talking about single enum cases with multiple associated values being represented by a tuple.

The root values of tuples and structs are product types while enums are sum types. In general, anything with multiple configurable values at the same time are representable by tuples, and argument lists are no exception.

Having the compiler perform reliably is the most basic of user ergonomics, wouldn’t you say?

Can you clarify what you mean by "reliably"? My understanding was the compiler performed reliably, but more slowly, with more tech debt. Regardless, why not explore paths that improve both and aren't trade-offs?

The separation of argument lists and tuples was approached in a very specific way to eliminate a very specific ambiguity, but the data structures are the same. Susan's proposal is an elegant step towards fixing some of the issues we've been having while specifically preventing previous ambiguities.

Stephen

···

On Jun 7, 2017, at 2:01 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

>
> Those are enums, which were aligned with argument lists instead of
tuples in yet another proposal, SE-0155.

Can you clarify? I wasn't referring to enums, and SE-0155 starts by
talking about single enum cases with multiple associated values being
represented by a tuple.

Swift package descriptions make extensive use of enums. For example,
.target(...) is an enum case with an associated value, unless I’m mistaken.
I too got the order of “dependencies” and “path” reversed the other day; if
enum associated values were tuples, then it would not have mattered. I
assume this is what you were referring to above.

The root values of tuples and structs are product types while enums are sum

types. In general, anything with multiple configurable values at the same
time are representable by tuples, and argument lists are no exception.

An arbitrary argument list cannot be represented as a tuple. For example,
tuples cannot have elements of variadic type or inout type.

Having the compiler perform reliably is the most basic of user
ergonomics, wouldn’t you say?

Can you clarify what you mean by "reliably"? My understanding was the
compiler performed reliably, but more slowly, with more tech debt.

Slow, and especially inconsistently slow, performance fits the definition
of unreliable, wouldn’t you agree?

Regardless, why not explore paths that improve both and aren't trade-offs?

The separation of argument lists and tuples was approached in a very
specific way to eliminate a very specific ambiguity, but the data
structures are the same.

They are not.

Susan's proposal is an elegant step towards fixing some of the issues we've

···

On Wed, Jun 7, 2017 at 07:16 Stephen Celis <stephen.celis@gmail.com> wrote:

> On Jun 7, 2017, at 2:01 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
been having while specifically preventing previous ambiguities.

Stephen

Here is the full extent of the remarquable Swift 3 ergonomics. This full snippet compiles in Swift 3:

    func sum1(_ lhs: Int, _ rhs: Int) -> Int { return lhs + rhs }
    func sum2(lhs: Int, rhs: Int) -> Int { return lhs + rhs }
    func sum3(tuple: (Int, Int)) -> Int { return tuple.0 + tuple.1 }
    func sum4(tuple: (lhs: Int, rhs: Int)) -> Int { return tuple.lhs + tuple.rhs }

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }
    f1 { lhs, rhs in lhs + rhs }
    f1 { (lhs, rhs) in lhs + rhs }
    f1 { tuple in tuple.0 + tuple.1 }
    f1 { (tuple) in tuple.0 + tuple.1 }
    f1(+)
    f1(sum1)
    f1(sum2)
    f1(sum3)
    f1(sum4)
    
    // two arguments, with documentation names: identical
    func f2(_ closure: (_ a: Int, _ b: Int) -> Int) { closure(1, 2) }
    f2 { lhs, rhs in lhs + rhs }
    f2 { (lhs, rhs) in lhs + rhs }
    f2 { tuple in tuple.0 + tuple.1 }
    f2 { (tuple) in tuple.0 + tuple.1 }
    f2(+)
    f2(sum1)
    f2(sum2)
    f2(sum3)
    f2(sum4)
    
    // one tuple argument
    func f3(_ closure: ((Int, Int)) -> Int) { closure((1, 2)) }
    f3 { lhs, rhs in lhs + rhs }
    f3 { (lhs, rhs) in lhs + rhs }
    f3 { tuple in tuple.0 + tuple.1 }
    f3 { (tuple) in tuple.0 + tuple.1 }
    f3(+)
    f3(sum1)
    f3(sum2)
    f3(sum3)
    f3(sum4)
    
    // one keyed tuple argument
    func f4(_ closure: ((a: Int, b: Int)) -> Int) { closure((a: 1, b: 2)) }
    f4 { lhs, rhs in lhs + rhs }
    f4 { (lhs, rhs) in lhs + rhs }
    f4 { tuple in tuple.a + tuple.b }
    f4 { (tuple) in tuple.a + tuple.b }
    f4(+)
    f4(sum1)
    f4(sum2)
    f4(sum3)
    f4(sum4)

Gwendal

···

Le 7 juin 2017 à 12:33, Gwendal Roué <gwendal.roue@gmail.com> a écrit :

Le 7 juin 2017 à 12:03, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Well please no:

>> let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

Despite Chris Lattern being a semi-god, his double-parenthesis suggestion cruelly lacks in terms of user ergonomics. The compiler should be able to deal with the following code snippet, just like Swift 3 does:

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }
[...]

Answer inlined:
Am 7. Juni 2017 um 12:33:51, Gwendal Roué (gwendal.roue@gmail.com) schrieb:

···

Le 7 juin 2017 à 12:03, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> a écrit :

Well please no:

let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

Despite Chris Lattern being a semi-god, his double-parenthesis suggestion cruelly lacks in terms of user ergonomics.
Well it’s clearly not the ideal solution from a visual perspective but it’s an unambiguous one. If tuples were represented differently, let’s say `@(a, b)` we wouldn’t sit here and debate that long because it becomes crystal clear what should be possible and what not.

The compiler should be able to deal with the following code snippet, just like Swift 3 does:

// two arguments
func f1\(\_ closure: \(Int, Int\) \-&gt; Int\) \{ closure\(1, 2\) \}
f1 \{ lhs, rhs in lhs \+ rhs \}
f1 \{ \(lhs, rhs\) in lhs \+ rhs \}

Next one is a bug to me:

f1 \{ tuple in tuple\.0 \+ tuple\.1 \}

Fine:

f1\(\+\)

// two arguments, with documentation names
func f2\(\_ closure: \(\_ a: Int, \_ b: Int\) \-&gt; Int\) \{ closure\(1, 2\) \}
f2 \{ lhs, rhs in lhs \+ rhs \}
f2 \{ \(lhs, rhs\) in lhs \+ rhs \}

Another bug to me:

f2 \{ tuple in tuple\.0 \+ tuple\.1 \}

Fine:

f2\(\+\)

// one tuple argument
func f3\(\_ closure: \(\(Int, Int\)\) \-&gt; Int\) \{ closure\(\(1, 2\)\) \}

Should be `((lhs, rhs)) in` which eliminates both of the next two scenarios completely:

f3 \{ lhs, rhs in lhs \+ rhs \}
f3 \{ \(lhs, rhs\) in lhs \+ rhs \}

Fine:

f3 \{ tuple in tuple\.0 \+ tuple\.1 \}

Should be an error:

f3\(\+\)

See `f3`

// one keyed tuple argument
func f4\(\_ closure: \(\(a: Int, b: Int\)\) \-&gt; Int\) \{ closure\(\(a: 1, b: 2\)\) \}
f4 \{ lhs, rhs in lhs \+ rhs \}
f4 \{ \(lhs, rhs\) in lhs \+ rhs \}
f4 \{ tuple in tuple\.a \+ tuple\.b \}
f4\(\+\)

This covers the Swift 3 regressions developped by Stephen Celis and I, unless I have missed any. And this should be the goal of the designers of this language, *regardless of the actual types*. Developers are olding their breath: please just make this happen.

Now Swift 3 may have an issue with $n parameters. Here is my suggestion, should `((Int, Int)) -> Int` be different from `(Int, Int) -> Int`:

That’s what I would expect.

f1 \{ $0 \+ $1 \} // OK
f1 \{ $0\.0 \+ $0\.1 \} // OK in Swift 3, compiler error in Swift 4?

f2 \{ $0 \+ $1 \} // OK
f2 \{ $0\.0 \+ $0\.1 \} // OK in Swift 3, compiler error in Swift 4?

f3 \{ $0 \+ $1 \} // OK in Swift 3, compiler error in Swift 4?
f3 \{ $0\.0 \+ $0\.1 \} // OK

f4 \{ $0 \+ $1 \} // OK in Swift 3, compiler error in Swift 4?
f4 \{ $0\.a \+ $0\.b \} // OK

Gwendal Roué

These rules should as strict as they possible can be now. Later on we can add sugar to relax some of them. Here is a quote, again from Chris Lattner:

The art of evolving Swift forward is to carefully pick and choose areas to focus on, both because we want to ensure a coherent language, but also because implementor bandwidth is limited.

Beyond that, though syntactic sugar is always motivated by strong rationale and use-cases, in my opinion, it is the most dangerous kind of additive feature to consider at this point of Swift’s evolution. While these features are useful, they also clearly add complexity to the language, and it is difficult to gauge whether the problem being addressed will even exist in Swift as the other bigger features come in (for example, a macro system). These features can also make future language evolution more problematic because they consume syntactic real estate in the language.

If you’re into analogies, I see features like the generics improvements, ownership model, concurrency model, macro system, new frameworks, and other large scale efforts as the “bricks" that make up the house of Swift. In that analogy, syntactic sugar proposals are “mortar” that fills in the cracks between the bricks. If we add too much mortar too early on, we run the risk of the house of Swift being built out of mortar, or of not being able to fit the bricks into the right places. That would be very bad, given that we all want the house of Swift to be strong and beautiful over the long term.

-Chris

I disagree it almost feels like a revert of SE–0110. If we had more than only:

parentheses
(angle) brackets
braces
A fifth set, that would also look visually fine with tuples. This discussion would not even exist, no matter if the data structure behind the scenes is the same or not. Parameter list should stay as a list of types for a closure, nothing more nothing less (it can be baked as a tuple behind the scenes, but that’s not the main discussion here).

( âČ Int, Int ❳ ) -> Void

is different from

( Int, Int ) -> Void

···

--
Adrian Zubarev
Sent with Airmail

Am 7. Juni 2017 um 14:17:05, Stephen Celis via swift-evolution (swift-evolution@swift.org) schrieb:

Susan's proposal is an elegant step towards fixing some of the issues we've been having while specifically preventing previous ambiguities

I don't think it's a roll back change.

The proposal is not proposing allow to reorder the parameters by using the
tuple or implicit tuple splat to fit the function parameters.
It's just clarify the relationship of tuples and argument lists.

as the grammar of Swift 3, it's allowed wrapping the argument with a single
tuple to a function with two argument.

[(1, 2)].map(+)

and SE-0110 is the proposal make the distinguished and not allowed this
line of code.

although we fix the compiler to accept the destructuring of tuples

[(1, 2)].map({ ((lhs, rhs)) in lhs + rhs })

it's not accepting the code of [(1, 2)].map(+) which the operator + are not
function with accepting the tuple of two elements.

the only way we can thought is that the arguments with single tuple is
flattened and it's most compatible with Swift 3.

···

2017-06-07 13:05 GMT+08:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

This is not what was meant during discussion about re-evaluating SE-0110.
Tuples already behave as described, but function argument lists are not
tuples and have not been for a very long time: see SE-0029, SE-0066.

Also, consider SE-0046, which makes possible labels in single-argument
argument lists (not possible in tuples), and SE-0060, which prohibits
arbitrary reordering (although still possible in tuples). This is to say
that the whole direction of Swift since version 2 has been to erase the
historical relationship between tuples and argument lists.

The question is how to accommodate some common use cases for destructuring
as a matter of syntactic sugar after having carefully distinguished
argument lists and tuples in the compiler, which is a given, not how to
roll back a change that was settled in 2011, by Chris’s telling.

On Tue, Jun 6, 2017 at 23:14 Susan Cheng via swift-evolution < > swift-evolution@swift.org> wrote:

func add2(_ pair: (Int, Int)) -> Int {
    return pair.0 + pair.1
}

consider the follows

let _add2 = add2 // add2 have the typeof `((Int, Int)) -> Int`, it
should flatten to `(Int, Int) -> Int`

so these two lines are also acceptable
[(1, 2)].map(add1)
[(1, 2)].map(add2)

this proposal is not just changing the behaviour of closure, this
proposal also changing the tuple type

(((Int, Int))) flatten to (Int, Int)
(Int, (((Int, Int))), Int) flatten to (Int, (Int, Int), Int)

2017-06-07 12:03 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:

The inline cases make sense to me, but my concern for ambiguity are
because we can define both of these functions:

    func add1(_ x: Int, _ y: Int) -> Int {
      return x + y
    }
    func add2(_ pair: (Int, Int)) -> Int {
      return pair.0 + pair.1
    }

    // What's the behavior here?
    [(1, 2)].map(add1)
    [(1, 2)].map(add2)

What comes to mind, though, is, Swift already prevents single-element
tuples, so why not prevent functions that take a single tuple as the only
argument?

Swift could provide a fix-it for functions that take single tuples and
say: "Swift functions cannot take a single tuple. Please write a function
that takes as many arguments as the tuple specified."

Considering the fact that Swift generally operates in a multi-argument
world, and given that functions taking a single tuple are the minority, I
think this would be a good way to move forward.

Stephen

> On Jun 6, 2017, at 11:21 PM, Susan Cheng <susan.doggie@gmail.com> >>> wrote:
>
> [(1, 2)].map({ x, y in x + y }) // this line is correct
> [(1, 2)].map({ tuple in tuple.0 + tuple.1 }) // this line should not
accepted
>
> or
>
> [(1, 2)].map({ $0 + $1 }) // this line is correct
> [(1, 2)].map({ $0.0 + $0.1 }) // this line should not accepted
>
> it's because `((Int, Int)) -> Int` always flatten to `(Int, Int) ->
Int`
> so, it should only accept the function with two arguments
>
>
>
> 2017-06-07 11:07 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:
> I like this a lot, but how do we solve for the function case?
>
> func add(_ x: Int, _ y: Int) -> Int {
> return x + y
> }
> [(1, 2)].map(add)
>
> Where does `map` with a function of `((Int, Int)) -> Int` fit in?
>
> > On Jun 6, 2017, at 10:15 PM, Susan Cheng via swift-evolution < >>> swift-evolution@swift.org> wrote:
> >
> > Introduction
> >
> >
> > Because the painful of SE-0110, here is a proposal to clarify the
tuple syntax.
> >
> > Proposed solution
> >
> > 1. single element tuple always be flattened
> >
> > let tuple1: (((Int))) = 0 // TypeOf(tuple1) == Int
> >
> > let tuple2: ((((Int))), Int) = (0, 0) // TypeOf(tuple2) == (Int,
Int)
> >
> > 2. function arguments list also consider as a tuple, which means the
function that accept a single tuple should always be flattened.
> >
> > let fn1: (Int, Int) -> Void = { _, _ in }
> >
> > let fn2: ((Int, Int)) -> Void = { _, _ in } // always flattened
> >
> > let fn3: (Int, Int) -> Void = { _ in } // not allowed, here are two
arguments
> >
> > let fn4: ((Int, Int)) -> Void = { _ in } // not allowed, here are
two arguments
> >
> > _______________________________________________
> > 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

`((Int, Int)) -> Void` will be same type as `(Int, Int) -> Void`

···

2017-06-07 18:09 GMT+08:00 Adrian Zubarev <adrian.zubarev@devandartist.com>:

Keep in mind there is also SE–0111 cometary which promises sugar for
parameter labels for closures:

// **
let foo(tuple:): ((Int, Int)) -> Void

// Sugar for **
let foo: (tuple: (Int, Int)) -> Void

What will happen if you’d always flatten here?

--
Adrian Zubarev
Sent with Airmail

Am 7. Juni 2017 um 12:03:08, Adrian Zubarev (adrian.zubarev@devandartist.
com) schrieb:

Well please no:

let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other
thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

That’s a correct error:

let fn3: (Int, Int) -> Void = { _ in }

This should be allowed, because we might want to work with the whole tuple
and not a desctructured elements only:

let fn4: ((Int, Int)) -> Void = { tuple in }

--
Adrian Zubarev
Sent with Airmail

    Just a thought

    if parentheses is important, why the tuples are not?

This is stated on the proposal (and not in previous proposals):

https://github.com/apple/swift-evolution/blob/master/proposals/0110-distingish-single-tuple-arg.md

  *

    We understand that this may be a departure from the current convention that a set
    of parentheses enclosing a single object are considered semantically meaningless,
    but it is the most natural way to differentiate between the two situations
    described above and would be a clearly-delineated one-time-only exception.

    <https://github.com/apple/swift-evolution/blob/master/proposals/0110-distingish-single-tuple-arg.md#impact-on-existing-code&gt;

This proposal marks a one-time-only exception, to differentiate the parenthesis needed to enclose a list of closure parameters and the parenthesis needed for tuples. That's adding an exception for implementing a regression.

The more I think about it, the more I hate* this proposal.

* Well, not _hate_, let's say slightly dislike :P

Please look here:

into "Proposed solution" section:

Parentheses will be required in function types. Examples:

Int -> Int // error
(Int) -> Int // function from Int to Int
((Int)) -> Int // also function from Int to Int

Int, Int -> Int // error
(Int, Int) -> Int // function from Int and Int to Int
((Int, Int)) -> Int // function from tuple (Int, Int) to Int

let f: () -> Int // function with no parameters
let g: (()) -> Int // function taking a single () parameter
let h: ((())) -> Int // function taking a single () parameter

f(); g(()); h(()) // correct
f(()); g(); h() // errors

Do you also hate* SE-0066?

···

On 08.06.2017 12:17, VĂ­ctor Pimentel RodrĂ­guez via swift-evolution wrote:

On Thu, Jun 8, 2017 at 5:15 AM, Susan Cheng via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
VĂ­ctor Pimentel

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

At first glance, this makes totally sense to me, feel free to push this idea on the main discussion thread about this dilemma. :)

···

--
Adrian Zubarev
Sent with Airmail

Am 8. Juni 2017 um 19:41:03, Brent Royal-Gordon (brent@architechies.com) schrieb:

On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Well please no:

let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

I think this suggestion is better than the status quo. I'm wondering, though, if we should just drop the outer set of parentheses entirely, unless you're also putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can look like this:

{ (x: Int, y: Int) in 
 }

Or it can look like this:

{ x, y in 
 }

But it *cannot* look like this:

{ (x, y) in 
 }

The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, which immediately destructures the tuple parameter into separate constants.

--
Brent Royal-Gordon
Architechies

Hello,

There's a difference, in the mind of people here that try to show how bad were the recent changes, between:

1: closures defined independently
2: closures given as a parameter to a function.

I think that we all agree that the type of a closure that is defined independently should be well defined:

    // Choose between (Int, Int) -> () or ((x: Int, y: Int)) -> ()
    let a = { (x: Int, y: Int) -> Int in ... }
    let b = { ((x: Int, y: Int)) -> Int in ... }

However, when a closure is given as an argument of a function that expects a closure, we ask for the maximum possible flexibility, as Swift 3 did:

    func wantsTwoArguments(_ closure: (Int, Int) -> Int) { closure(1, 2) }
    wantsTwoArguments { a, b in a + b }
    wantsTwoArguments { (a, b) in a + b }
    wantsTwoArguments { t in t.0 + t.1 } // OK, maybe not

    func wantsATupleArgument(_ closure: ((Int, Int)) -> Int) { closure((1, 2)) }
    wantsATupleArgument { a, b in a + b }
    wantsATupleArgument { (a, b) in a + b }
    wantsATupleArgument { t in t.0 + t.1 }
    
    func wantsANamedTupleArgument(_ closure: ((lhs: Int, rhs: Int)) -> Int) { closure((lhs: 1, rhs: 2)) }
    wantsANamedTupleArgument { a, b in a + b }
    wantsANamedTupleArgument { (a, b) in a + b }
    wantsANamedTupleArgument { t in t.lhs + t.rhs }

This gives us the ability to deal with unfitted function signatures. For example, most Dictionary methods. Yes, they are usually unfitted:

    extension Dictionary {
        func forEach(_ body: ((key: Key, value: Value)) throws -> Void) rethrows
    }

Who cares about this named (key:value:) tuple? Absolutely nobody, as exemplified by this remarquable Swift 3 snippet below, where no tuple, no `key`, and no `value` is in sight:

    let scores: [String: Int] = ... // [playerName: score]
    scores.forEach { name, score in
        print("\(name): \(score)")
    }

Do you see?

Gwendal

···

Le 8 juin 2017 à 19:40, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Well please no:

>> let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

I think this suggestion is better than the status quo. I'm wondering, though, if we should just drop the outer set of parentheses entirely, unless you're also putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can look like this:

  { (x: Int, y: Int) in 
 }

Or it can look like this:

  { x, y in 
 }

But it *cannot* look like this:

  { (x, y) in 
 }

The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, which immediately destructures the tuple parameter into separate constants.

--
Brent Royal-Gordon
Architechies

I better understand your message. It does look good at first sight.

But... The full picture needs to be completed with closures of type `((Int, Int)) -> T` and `((lsh: Int, rhs: Int)) -> T`. Those are among the most *interesting* in the regressions they brought.

Will there be any forbidden syntax as well? What about unfitted signatures, which I talked just before?

Gwendal

···

Le 8 juin 2017 à 19:40, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Well please no:

>> let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

I think this suggestion is better than the status quo. I'm wondering, though, if we should just drop the outer set of parentheses entirely, unless you're also putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can look like this:

  { (x: Int, y: Int) in 
 }

Or it can look like this:

  { x, y in 
 }

But it *cannot* look like this:

  { (x, y) in 
 }

The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, which immediately destructures the tuple parameter into separate constants.

Well please no:

|let fn2: ((Int, Int)) -> Void = { lhs, rhs in }|

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

>let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }|

I think this suggestion is better than the status quo. I'm wondering, though, if we should just drop the outer set of parentheses entirely, unless you're also putting types on the parameters. That is, a closure of type `(Int, Int) -> T` can look like this:

{ (x: Int, y: Int) in 
 }

Or it can look like this:

{ x, y in 
 }

But it *cannot* look like this:

{ (x, y) in 
 }

The `(x, y)` form can instead be a closure of a type like `((Int, Int)) -> T`, which immediately destructures the tuple parameter into separate constants.

The main problem with this suggestion, as I understand, is that {(x,y) in ..} in Swift 3 is allowed for (Int,Int)->() closure. So, your proposal will also broke code during the migration as ((Int,Int))->() will be sent where (Int,Int)->() is expected.

Also, personally, I see a big inconsistency when
{ (x: Int, y: Int) in ..}
declares closure of type (Int,Int)->(), but
{ (x, y) in ... }
for some reason declared closure of type ((Int,Int))->()
while currently we know that we are allowed to just omit type annotation in closures.

And it seems naturally for me to see { ((x,y)) in ..} for closure of type ((Int,Int))->() - there is nice symmetry about these two pairs of parenthesis in closure and in type syntax itself.

···

On 08.06.2017 20:40, Brent Royal-Gordon via swift-evolution wrote:

On Jun 7, 2017, at 3:03 AM, Adrian Zubarev via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
Brent Royal-Gordon
Architechies

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

I don't think it's a roll back change.

Function argument lists were once tuples, but they have not been for many
years. All of the proposals I listed above proceed on the basis that these
are not the same. Your proposal says that function argument lists should be
tuples: that is rolling back a >5 year old change, is it not?

The proposal is not proposing allow to reorder the parameters by using the

···

On Wed, Jun 7, 2017 at 01:51 Susan Cheng <susan.doggie@gmail.com> wrote:

tuple or implicit tuple splat to fit the function parameters.
It's just clarify the relationship of tuples and argument lists.

as the grammar of Swift 3, it's allowed wrapping the argument with a
single tuple to a function with two argument.

[(1, 2)].map(+)

and SE-0110 is the proposal make the distinguished and not allowed this
line of code.

although we fix the compiler to accept the destructuring of tuples

[(1, 2)].map({ ((lhs, rhs)) in lhs + rhs })

it's not accepting the code of [(1, 2)].map(+) which the operator + are
not function with accepting the tuple of two elements.

the only way we can thought is that the arguments with single tuple is
flattened and it's most compatible with Swift 3.

2017-06-07 13:05 GMT+08:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

This is not what was meant during discussion about re-evaluating SE-0110.
Tuples already behave as described, but function argument lists are not
tuples and have not been for a very long time: see SE-0029, SE-0066.

Also, consider SE-0046, which makes possible labels in single-argument
argument lists (not possible in tuples), and SE-0060, which prohibits
arbitrary reordering (although still possible in tuples). This is to say
that the whole direction of Swift since version 2 has been to erase the
historical relationship between tuples and argument lists.

The question is how to accommodate some common use cases for
destructuring as a matter of syntactic sugar after having carefully
distinguished argument lists and tuples in the compiler, which is a given,
not how to roll back a change that was settled in 2011, by Chris’s telling.

On Tue, Jun 6, 2017 at 23:14 Susan Cheng via swift-evolution < >> swift-evolution@swift.org> wrote:

func add2(_ pair: (Int, Int)) -> Int {
    return pair.0 + pair.1
}

consider the follows

let _add2 = add2 // add2 have the typeof `((Int, Int)) -> Int`, it
should flatten to `(Int, Int) -> Int`

so these two lines are also acceptable
[(1, 2)].map(add1)
[(1, 2)].map(add2)

this proposal is not just changing the behaviour of closure, this
proposal also changing the tuple type

(((Int, Int))) flatten to (Int, Int)
(Int, (((Int, Int))), Int) flatten to (Int, (Int, Int), Int)

2017-06-07 12:03 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:

The inline cases make sense to me, but my concern for ambiguity are
because we can define both of these functions:

    func add1(_ x: Int, _ y: Int) -> Int {
      return x + y
    }
    func add2(_ pair: (Int, Int)) -> Int {
      return pair.0 + pair.1
    }

    // What's the behavior here?
    [(1, 2)].map(add1)
    [(1, 2)].map(add2)

What comes to mind, though, is, Swift already prevents single-element
tuples, so why not prevent functions that take a single tuple as the only
argument?

Swift could provide a fix-it for functions that take single tuples and
say: "Swift functions cannot take a single tuple. Please write a function
that takes as many arguments as the tuple specified."

Considering the fact that Swift generally operates in a multi-argument
world, and given that functions taking a single tuple are the minority, I
think this would be a good way to move forward.

Stephen

> On Jun 6, 2017, at 11:21 PM, Susan Cheng <susan.doggie@gmail.com> >>>> wrote:
>
> [(1, 2)].map({ x, y in x + y }) // this line is correct
> [(1, 2)].map({ tuple in tuple.0 + tuple.1 }) // this line should
not accepted
>
> or
>
> [(1, 2)].map({ $0 + $1 }) // this line is correct
> [(1, 2)].map({ $0.0 + $0.1 }) // this line should not accepted
>
> it's because `((Int, Int)) -> Int` always flatten to `(Int, Int) ->
Int`
> so, it should only accept the function with two arguments
>
>
>
> 2017-06-07 11:07 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:
> I like this a lot, but how do we solve for the function case?
>
> func add(_ x: Int, _ y: Int) -> Int {
> return x + y
> }
> [(1, 2)].map(add)
>
> Where does `map` with a function of `((Int, Int)) -> Int` fit in?
>
> > On Jun 6, 2017, at 10:15 PM, Susan Cheng via swift-evolution < >>>> swift-evolution@swift.org> wrote:
> >
> > Introduction
> >
> >
> > Because the painful of SE-0110, here is a proposal to clarify the
tuple syntax.
> >
> > Proposed solution
> >
> > 1. single element tuple always be flattened
> >
> > let tuple1: (((Int))) = 0 // TypeOf(tuple1) == Int
> >
> > let tuple2: ((((Int))), Int) = (0, 0) // TypeOf(tuple2) == (Int,
Int)
> >
> > 2. function arguments list also consider as a tuple, which means
the function that accept a single tuple should always be flattened.
> >
> > let fn1: (Int, Int) -> Void = { _, _ in }
> >
> > let fn2: ((Int, Int)) -> Void = { _, _ in } // always flattened
> >
> > let fn3: (Int, Int) -> Void = { _ in } // not allowed, here are
two arguments
> >
> > let fn4: ((Int, Int)) -> Void = { _ in } // not allowed, here are
two arguments
> >
> > _______________________________________________
> > 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

Swift package descriptions make extensive use of enums. For example, .target(...) is an enum case with an associated value, unless I’m mistaken. I too got the order of “dependencies” and “path” reversed the other day; if enum associated values were tuples, then it would not have mattered. I assume this is what you were referring to above.

Enum associated values are isomorphic to tuples. Function arguments are also isomorphic to tuples. You're referring to a specific ordering issue that exists in both enum cases with multiple associated values, functions arguments (like a struct initializer), and tuples.

An arbitrary argument list cannot be represented as a tuple. For example, tuples cannot have elements of variadic type or inout type.

Modifiers like throws/inout/@escaping affect the body of the function and are arguably erasable in the data structure itself.

Variadic tuples are disallowed but not an impossible concept (and supporting them hasn't been ruled out as far as I can remember).

Slow, and especially inconsistently slow, performance fits the definition of unreliable, wouldn’t you agree?

Again, trade-offs and openness to exploring better solutions.

They are not.

Other than the function body modifiers, how are they not?

Stephen

···

On Jun 7, 2017, at 8:27 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

IMO, if tuples and argument lists are to be distinguished in any way, it is
imperative that f3(+) and f4(+), and some of your other examples, _not_
work.

After all, if a tuple is not an argument list, it should possible to have a
function of type ((Int, Int)) -> Int and a function of type (Int, Int) ->
Int share the same name (IIUC, it’s a known bug that this does not
currently work). Quite simply, there’s a type mismatch if you pass sum1 to
f3–what happens if there’s a distinct, overloaded sum1 that takes a single
tuple?

Chris’s suggestion restores a syntactic convenience without touching the
type system. What you are arguing for is essentially making ((Int, Int)) ->
Int and (Int, Int) -> Int synonymous again, either in some or in all
contexts.

···

On Wed, Jun 7, 2017 at 05:41 Gwendal Roué via swift-evolution < swift-evolution@swift.org> wrote:

Le 7 juin 2017 à 12:33, Gwendal Roué <gwendal.roue@gmail.com> a écrit :

Le 7 juin 2017 à 12:03, Adrian Zubarev via swift-evolution < > swift-evolution@swift.org> a écrit :

Well please no:

let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other
thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

Despite Chris Lattern being a semi-god, his double-parenthesis suggestion
cruelly lacks in terms of user ergonomics. The compiler should be able to
deal with the following code snippet, just like Swift 3 does:

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }

[...]

Here is the full extent of the remarquable Swift 3 ergonomics. This full
snippet compiles in Swift 3:

    func sum1(_ lhs: Int, _ rhs: Int) -> Int { return lhs + rhs }
    func sum2(lhs: Int, rhs: Int) -> Int { return lhs + rhs }
    func sum3(tuple: (Int, Int)) -> Int { return tuple.0 + tuple.1 }
    func sum4(tuple: (lhs: Int, rhs: Int)) -> Int { return tuple.lhs +
tuple.rhs }

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }
    f1 { lhs, rhs in lhs + rhs }
    f1 { (lhs, rhs) in lhs + rhs }
    f1 { tuple in tuple.0 + tuple.1 }
    f1 { (tuple) in tuple.0 + tuple.1 }
    f1(+)
    f1(sum1)
    f1(sum2)
    f1(sum3)
    f1(sum4)

    // two arguments, with documentation names: identical
    func f2(_ closure: (_ a: Int, _ b: Int) -> Int) { closure(1, 2) }
    f2 { lhs, rhs in lhs + rhs }
    f2 { (lhs, rhs) in lhs + rhs }
    f2 { tuple in tuple.0 + tuple.1 }
    f2 { (tuple) in tuple.0 + tuple.1 }
    f2(+)
    f2(sum1)
    f2(sum2)
    f2(sum3)
    f2(sum4)

    // one tuple argument
    func f3(_ closure: ((Int, Int)) -> Int) { closure((1, 2)) }
    f3 { lhs, rhs in lhs + rhs }
    f3 { (lhs, rhs) in lhs + rhs }
    f3 { tuple in tuple.0 + tuple.1 }
    f3 { (tuple) in tuple.0 + tuple.1 }
    f3(+)
    f3(sum1)
    f3(sum2)
    f3(sum3)
    f3(sum4)

    // one keyed tuple argument
    func f4(_ closure: ((a: Int, b: Int)) -> Int) { closure((a: 1, b: 2))
}
    f4 { lhs, rhs in lhs + rhs }
    f4 { (lhs, rhs) in lhs + rhs }
    f4 { tuple in tuple.a + tuple.b }
    f4 { (tuple) in tuple.a + tuple.b }
    f4(+)
    f4(sum1)
    f4(sum2)
    f4(sum3)
    f4(sum4)

Gwendal

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

Well I’m clearly against this, it doesn’t hurt to use some more paeans inside a closure to tell the compiler if you want to destructure a tuple or use a single argument for the whole tuple.

···

--
Adrian Zubarev
Sent with Airmail

Am 7. Juni 2017 um 12:31:47, Susan Cheng (susan.doggie@gmail.com) schrieb:

`((Int, Int)) -> Void` will be same type as `(Int, Int) -> Void`

2017-06-07 18:09 GMT+08:00 Adrian Zubarev <adrian.zubarev@devandartist.com>:
Keep in mind there is also SE–0111 cometary which promises sugar for parameter labels for closures:

// **
let foo(tuple:): ((Int, Int)) -> Void

// Sugar for **
let foo: (tuple: (Int, Int)) -> Void
What will happen if you’d always flatten here?

--
Adrian Zubarev
Sent with Airmail

Am 7. Juni 2017 um 12:03:08, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Well please no:

let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

That’s a correct error:

let fn3: (Int, Int) -> Void = { _ in }
This should be allowed, because we might want to work with the whole tuple and not a desctructured elements only:

let fn4: ((Int, Int)) -> Void = { tuple in }

--
Adrian Zubarev
Sent with Airmail

>
> Swift package descriptions make extensive use of enums. For example,
.target(...) is an enum case with an associated value, unless I’m mistaken.
I too got the order of “dependencies” and “path” reversed the other day; if
enum associated values were tuples, then it would not have mattered. I
assume this is what you were referring to above.

Enum associated values are isomorphic to tuples. Function arguments are
also isomorphic to tuples. You're referring to a specific ordering issue
that exists in both enum cases with multiple associated values, functions
arguments (like a struct initializer), and tuples.

I’m not sure what you’re getting at. My point is that tuples currently
allow “shuffling” with labels but argument lists do not, and that enum
associated values were recently revised to be initialized with an argument
list and not a tuple, although that proposal is not fully implemented.

An arbitrary argument list cannot be represented as a tuple. For example,
tuples cannot have elements of variadic type or inout type.

Modifiers like throws/inout/@escaping affect the body of the function and
are arguably erasable in the data structure itself.

An early proposal moved the placement of inout to reflect the fact that
`inout T` is a distinct type from `T`. Escaping vs. non-escaping closures
also have type system significance, afaik; this is why
`withoutActuallyEscaping` required quite a bit of cleverness to implement
and cannot be expressed in Swift itself. In any case, the compiler itself
no longer represents argument lists as tuples, and that distinction is
fully plumbed through, unless I’m mistaken.

Variadic tuples are disallowed but not an impossible concept (and

supporting them hasn't been ruled out as far as I can remember).

> Slow, and especially inconsistently slow, performance fits the
definition of unreliable, wouldn’t you agree?

Again, trade-offs and openness to exploring better solutions.

> They are not.

Other than the function body modifiers, how are they not?

Literally, they are not the same, neither in syntax, nor in the way they
are modeled by the compiler.

Stephen

···

On Wed, Jun 7, 2017 at 07:40 Stephen Celis <stephen.celis@gmail.com> wrote:

> On Jun 7, 2017, at 8:27 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Xiaodi, Adrian, you are actively pushing so that something that was allowed, well compiled (no runtime issue), and covered actual uses cases, becomes forbidden. Without any developer advantage that would somehow balance the change.

That's called a regression.

And what's the rationale, already? A sense of compiler aesthetics? Since when a sense of compiler aesthetics is more valuable than a sense of code aesthetics? Aren't both supposed to walk together as a pair?

Gwendal

···

Le 7 juin 2017 à 12:54, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

IMO, if tuples and argument lists are to be distinguished in any way, it is imperative that f3(+) and f4(+), and some of your other examples, _not_ work.

After all, if a tuple is not an argument list, it should possible to have a function of type ((Int, Int)) -> Int and a function of type (Int, Int) -> Int share the same name (IIUC, it’s a known bug that this does not currently work). Quite simply, there’s a type mismatch if you pass sum1 to f3–what happens if there’s a distinct, overloaded sum1 that takes a single tuple?

Chris’s suggestion restores a syntactic convenience without touching the type system. What you are arguing for is essentially making ((Int, Int)) -> Int and (Int, Int) -> Int synonymous again, either in some or in all contexts.

On Wed, Jun 7, 2017 at 05:41 Gwendal Roué via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Le 7 juin 2017 à 12:33, Gwendal Roué <gwendal.roue@gmail.com <mailto:gwendal.roue@gmail.com>> a écrit :

Le 7 juin 2017 à 12:03, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Well please no:

>>> let fn2: ((Int, Int)) -> Void = { lhs, rhs in }

Instead use destructuring sugar pitched by Chris Lattner on the other thread:

let fn2: ((Int, Int)) -> Void = { ((lhs, rhs)) in }

Despite Chris Lattern being a semi-god, his double-parenthesis suggestion cruelly lacks in terms of user ergonomics. The compiler should be able to deal with the following code snippet, just like Swift 3 does:

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }

[...]

Here is the full extent of the remarquable Swift 3 ergonomics. This full snippet compiles in Swift 3:

    func sum1(_ lhs: Int, _ rhs: Int) -> Int { return lhs + rhs }
    func sum2(lhs: Int, rhs: Int) -> Int { return lhs + rhs }
    func sum3(tuple: (Int, Int)) -> Int { return tuple.0 + tuple.1 }
    func sum4(tuple: (lhs: Int, rhs: Int)) -> Int { return tuple.lhs + tuple.rhs }

    // two arguments
    func f1(_ closure: (Int, Int) -> Int) { closure(1, 2) }
    f1 { lhs, rhs in lhs + rhs }
    f1 { (lhs, rhs) in lhs + rhs }
    f1 { tuple in tuple.0 + tuple.1 }
    f1 { (tuple) in tuple.0 + tuple.1 }
    f1(+)
    f1(sum1)
    f1(sum2)
    f1(sum3)
    f1(sum4)
    
    // two arguments, with documentation names: identical
    func f2(_ closure: (_ a: Int, _ b: Int) -> Int) { closure(1, 2) }
    f2 { lhs, rhs in lhs + rhs }
    f2 { (lhs, rhs) in lhs + rhs }
    f2 { tuple in tuple.0 + tuple.1 }
    f2 { (tuple) in tuple.0 + tuple.1 }
    f2(+)
    f2(sum1)
    f2(sum2)
    f2(sum3)
    f2(sum4)
    
    // one tuple argument
    func f3(_ closure: ((Int, Int)) -> Int) { closure((1, 2)) }
    f3 { lhs, rhs in lhs + rhs }
    f3 { (lhs, rhs) in lhs + rhs }
    f3 { tuple in tuple.0 + tuple.1 }
    f3 { (tuple) in tuple.0 + tuple.1 }
    f3(+)
    f3(sum1)
    f3(sum2)
    f3(sum3)
    f3(sum4)
    
    // one keyed tuple argument
    func f4(_ closure: ((a: Int, b: Int)) -> Int) { closure((a: 1, b: 2)) }
    f4 { lhs, rhs in lhs + rhs }
    f4 { (lhs, rhs) in lhs + rhs }
    f4 { tuple in tuple.a + tuple.b }
    f4 { (tuple) in tuple.a + tuple.b }
    f4(+)
    f4(sum1)
    f4(sum2)
    f4(sum3)
    f4(sum4)

Gwendal

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

Argument lists should or shouldn't be tuples. I think the question likes a
concept more than a technical question.

···

2017-06-07 14:56 GMT+08:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

On Wed, Jun 7, 2017 at 01:51 Susan Cheng <susan.doggie@gmail.com> wrote:

I don't think it's a roll back change.

Function argument lists were once tuples, but they have not been for many
years. All of the proposals I listed above proceed on the basis that these
are not the same. Your proposal says that function argument lists should be
tuples: that is rolling back a >5 year old change, is it not?

The proposal is not proposing allow to reorder the parameters by using the

tuple or implicit tuple splat to fit the function parameters.
It's just clarify the relationship of tuples and argument lists.

as the grammar of Swift 3, it's allowed wrapping the argument with a
single tuple to a function with two argument.

[(1, 2)].map(+)

and SE-0110 is the proposal make the distinguished and not allowed this
line of code.

although we fix the compiler to accept the destructuring of tuples

[(1, 2)].map({ ((lhs, rhs)) in lhs + rhs })

it's not accepting the code of [(1, 2)].map(+) which the operator + are
not function with accepting the tuple of two elements.

the only way we can thought is that the arguments with single tuple is
flattened and it's most compatible with Swift 3.

2017-06-07 13:05 GMT+08:00 Xiaodi Wu <xiaodi.wu@gmail.com>:

This is not what was meant during discussion about re-evaluating
SE-0110. Tuples already behave as described, but function argument lists
are not tuples and have not been for a very long time: see SE-0029, SE-0066.

Also, consider SE-0046, which makes possible labels in single-argument
argument lists (not possible in tuples), and SE-0060, which prohibits
arbitrary reordering (although still possible in tuples). This is to say
that the whole direction of Swift since version 2 has been to erase the
historical relationship between tuples and argument lists.

The question is how to accommodate some common use cases for
destructuring as a matter of syntactic sugar after having carefully
distinguished argument lists and tuples in the compiler, which is a given,
not how to roll back a change that was settled in 2011, by Chris’s telling.

On Tue, Jun 6, 2017 at 23:14 Susan Cheng via swift-evolution < >>> swift-evolution@swift.org> wrote:

func add2(_ pair: (Int, Int)) -> Int {
    return pair.0 + pair.1
}

consider the follows

let _add2 = add2 // add2 have the typeof `((Int, Int)) -> Int`,
it should flatten to `(Int, Int) -> Int`

so these two lines are also acceptable
[(1, 2)].map(add1)
[(1, 2)].map(add2)

this proposal is not just changing the behaviour of closure, this
proposal also changing the tuple type

(((Int, Int))) flatten to (Int, Int)
(Int, (((Int, Int))), Int) flatten to (Int, (Int, Int), Int)

2017-06-07 12:03 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:

The inline cases make sense to me, but my concern for ambiguity are
because we can define both of these functions:

    func add1(_ x: Int, _ y: Int) -> Int {
      return x + y
    }
    func add2(_ pair: (Int, Int)) -> Int {
      return pair.0 + pair.1
    }

    // What's the behavior here?
    [(1, 2)].map(add1)
    [(1, 2)].map(add2)

What comes to mind, though, is, Swift already prevents single-element
tuples, so why not prevent functions that take a single tuple as the only
argument?

Swift could provide a fix-it for functions that take single tuples and
say: "Swift functions cannot take a single tuple. Please write a function
that takes as many arguments as the tuple specified."

Considering the fact that Swift generally operates in a multi-argument
world, and given that functions taking a single tuple are the minority, I
think this would be a good way to move forward.

Stephen

> On Jun 6, 2017, at 11:21 PM, Susan Cheng <susan.doggie@gmail.com> >>>>> wrote:
>
> [(1, 2)].map({ x, y in x + y }) // this line is correct
> [(1, 2)].map({ tuple in tuple.0 + tuple.1 }) // this line should
not accepted
>
> or
>
> [(1, 2)].map({ $0 + $1 }) // this line is correct
> [(1, 2)].map({ $0.0 + $0.1 }) // this line should not accepted
>
> it's because `((Int, Int)) -> Int` always flatten to `(Int, Int) ->
Int`
> so, it should only accept the function with two arguments
>
>
>
> 2017-06-07 11:07 GMT+08:00 Stephen Celis <stephen.celis@gmail.com>:
> I like this a lot, but how do we solve for the function case?
>
> func add(_ x: Int, _ y: Int) -> Int {
> return x + y
> }
> [(1, 2)].map(add)
>
> Where does `map` with a function of `((Int, Int)) -> Int` fit in?
>
> > On Jun 6, 2017, at 10:15 PM, Susan Cheng via swift-evolution < >>>>> swift-evolution@swift.org> wrote:
> >
> > Introduction
> >
> >
> > Because the painful of SE-0110, here is a proposal to clarify the
tuple syntax.
> >
> > Proposed solution
> >
> > 1. single element tuple always be flattened
> >
> > let tuple1: (((Int))) = 0 // TypeOf(tuple1) == Int
> >
> > let tuple2: ((((Int))), Int) = (0, 0) // TypeOf(tuple2) == (Int,
Int)
> >
> > 2. function arguments list also consider as a tuple, which means
the function that accept a single tuple should always be flattened.
> >
> > let fn1: (Int, Int) -> Void = { _, _ in }
> >
> > let fn2: ((Int, Int)) -> Void = { _, _ in } // always flattened
> >
> > let fn3: (Int, Int) -> Void = { _ in } // not allowed, here are
two arguments
> >
> > let fn4: ((Int, Int)) -> Void = { _ in } // not allowed, here are
two arguments
> >
> > _______________________________________________
> > 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