[Draft] Tuple-Based Compound Optional Binding


(Brent Royal-Gordon) #1

When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding
Proposal: TBD
Author: Brent Royal-Gordon <https://github.com/brentdax>
Status: TBD
Review manager: TBD
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>Introduction

This proposal enhances optional binding with a new, tuple-based syntax for binding multiple values. It replaces functionality lost in SE-0099 with a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099 Restructuring Condition Clauses <http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }
This doesn't conflict with the clause separation syntax because the commas are within parentheses. However, the analogous syntax for optional bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
// error: initializer for conditional binding must have
// Optional type, not '(Int?, Int?, Int?)' (aka
// '(Optional<Int>, Optional<Int>, Optional<Int>)')
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed Solution

We should extend optional binding clauses to permit a tuple of optional values on the right of the = and a tuple of constants with identical arity on the left. Swift should test each element of the tuple on the right, and if none of them are nil, bind them to the constants on the left.

Nothing in this proposal should change the way optional binding handles an optional tuple (T, U)?, as opposed to a tuple of optionals (T?, U?). Even an optional tuple of optionals (T?, U?)? should continue to be handled as before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed Design

No change to the formal grammar is necessary, as the pattern and initializer productions in the optional-binding-head rule can already match tuples:

optional-binding-head : 'let' pattern initializer
Rather, Sema should be modified to detect this situation and generate appropriate code. Currently, TypeCheckPattern.cpp essentially converts let a = opt1 into case let a? = opt1; if this proposal is accepted, it should similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) = (opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge cases

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented by pattern matching, but is a niche feature. It should be supported if easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions returning tuples of optionals

Ideally, optional bindings whose initializer is an expression evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)
if let (a, b) = tuple { ... }
However, I'm not sure if Swift will have pinned down the type of the initializer at the point where it's generating the pattern. If this would be difficult or impossible to implement, Swift should continue to interpret code like this as attempting to bind an optional tuple, rather than a tuple of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name patterns

In theory, Swift could allow you to bind a tuple of optionals to a single constant:

if let tuple = (opt1, opt2) { ... }
However, this seems error-prone; the pattern doesn't draw a very clear picture of the value being operated upon, so you could easily misinterpret it as binding an optional tuple. Because of this ambiguity, Swift should always interpret this construct as binding an optional tuple, rejecting it with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact on Existing Code

This proposal is additive compared to SE-0099, but in combination with it, essentially replaces the Swift 2.2 compound binding syntax with a different, incompatible one. When moving directly from Swift 2.2, the migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }
Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
The "one-let-per-binding" syntax remains compatible with both Swift 2.2 and Swift 3, so projects which must support both can still perform multiple optional bindings in a single if statement without resorting to an #if swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives Considered

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing similar standard library functionality

Rather than including this functionality in the compiler, the standard library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }
public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }
// etc.
These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }
However, because we do not have variadic generics, we would need to provide a set of overloads for different arities, and our support would be limited to the arities we chose to provide. (Support for tuples, as opposed to separate parameters, would require a second set of overloads). Meanwhile, the tuple matching syntax is already precedented in case conditionals, so extending it seems pretty natural. Providing this in the compiler seems like the right solution.

···

--
Brent Royal-Gordon
Architechies


(Xiaodi Wu) #2

When I suggested this syntax in the acceptance thread for SE-0099, Chris
said it should be written up as a proposal. I'm sure this will get lost in
the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding

   - Proposal: TBD
   - Author: Brent Royal-Gordon <https://github.com/brentdax>
   - Status: TBD
   - Review manager: TBD

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>
Introduction

This proposal enhances optional binding with a new, tuple-based syntax for
binding multiple values. It replaces functionality lost in SE-0099 with a
syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099
Restructuring Condition Clauses
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>
Motivation

In Swift 2, it was possible to bind multiple optional values in a single if
let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }

SE-0099
<https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified
the syntax of conditional statements, but removed this feature so that , could
instead separate different conditional clauses. Code like this must now use
three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

The similar case clause sidesteps this problem because it can
pattern-match tuples. Hence, you can put several patterns in a tuple on the
left side of the =, and a matching number of values in a tuple on the
right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }

This doesn't conflict with the clause separation syntax because the commas
are within parentheses. However, the analogous syntax for optional bindings
is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }// error: initializer for conditional binding must have // Optional type, not '(Int?, Int?, Int?)' (aka // '(Optional<Int>, Optional<Int>, Optional<Int>)')

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed
Solution

We should extend optional binding clauses to permit a tuple of optional
values on the right of the = and a tuple of constants with identical
arity on the left. Swift should test each element of the tuple on the
right, and if none of them are nil, bind them to the constants on the
left.

Nothing in this proposal should change the way optional binding handles an *optional
tuple* (T, U)?, as opposed to a *tuple of optionals* (T?, U?). Even an
optional tuple of optionals (T?, U?)? should continue to be handled as
before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed
Design

No change to the formal grammar is necessary, as the *pattern* and
*initializer* productions in the *optional-binding-head* rule can already
match tuples:

optional-binding-head : 'let' pattern initializer

Rather, Sema should be modified to detect this situation and generate
appropriate code. Currently, TypeCheckPattern.cpp essentially converts let
a = opt1 into case let a? = opt1; if this proposal is accepted, it should
similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) =
(opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge
cases
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested
tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented
by pattern matching, but is a niche feature. It should be supported if
easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions
returning tuples of optionals

Ideally, optional bindings whose *initializer* is an expression
evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)if let (a, b) = tuple { ... }

However, I'm not sure if Swift will have pinned down the type of the
initializer at the point where it's generating the pattern. If this would
be difficult or impossible to implement, Swift should continue to interpret
code like this as attempting to bind an optional tuple, rather than a tuple
of optionals.

I think this proposal is definitely worthy of consideration (and
implementation). I hesitate that what works or not is being proposed partly
on the basis of whether it's difficult to implement or not. If I could
offer a suggestion as to exactly which edge cases should work, I think it
should be based on this guideline that can be articulated cleanly:

Given `if let (a, b, c)...` or any other variation or edge case, transform
in your mind's eye to `if case let (a?, b?, c?)...`. That is, append `case`
before `let` and then append `?` immediately after each constant so
introduced. If the `if case let...` version would work, so should the `if
let...` version. By that rule of thumb, `if case let (a?, (b?, c?)) =
(opt1, (opt2, opt3))` would work if I'm not mistaken, as would `if case let
(a?, b?) = tuple`, but `if case let tuple? = (opt1, opt2)` would not and
thus neither should `if let tuple = (opt1, opt2)`.

···

On Sun, Jun 12, 2016 at 6:46 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name
patterns

In theory, Swift could allow you to bind a tuple of optionals to a single
constant:

if let tuple = (opt1, opt2) { ... }

However, this seems error-prone; the pattern doesn't draw a very clear
picture of the value being operated upon, so you could easily misinterpret
it as binding an optional tuple. Because of this ambiguity, Swift should
always interpret this construct as binding an optional tuple, rejecting it
with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact
on Existing Code

This proposal is additive compared to SE-0099, but in combination with it,
essentially replaces the Swift 2.2 compound binding syntax with a
different, incompatible one. When moving directly from Swift 2.2, the
migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }

Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

The "one-let-per-binding" syntax remains compatible with both Swift 2.2
and Swift 3, so projects which must support both can still perform multiple
optional bindings in a single if statement without resorting to an #if
swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives
Considered
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not
accepting this proposal

This proposal does not add new functionality; it merely removes keyword
clutter. However, it offers a convenient replacement for a commonly-used
feature which has just been removed as a result of grammatical ambiguity,
not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing
similar standard library functionality

Rather than including this functionality in the compiler, the standard
library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }// etc.

These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }

However, because we do not have variadic generics, we would need to
provide a set of overloads for different arities, and our support would be
limited to the arities we chose to provide. (Support for tuples, as opposed
to separate parameters, would require a second set of overloads).
Meanwhile, the tuple matching syntax is already precedented in case conditionals,
so extending it seems pretty natural. Providing this in the compiler seems
like the right solution.

--
Brent Royal-Gordon
Architechies

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


(Pyry Jahkola) #3

You mention `guard case` in the motivation, but I think for the uninitiated reader it would be fair to point out that the following example already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

Aside of that, it's yet more magic to our `if let` syntax but I don't mind, it would be useful at times.

— Pyry

···

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }


(plx) #4

This proposal should specify if the tuple is evaluated “eagerly” or as an “early-exit”.

That is, if we write `if let (foo,bar,baz) = (_foo(), _bar(), _baz())`, and _bar() -> nil, will `_baz()` have been evaluated, or not?

I’d use this feature either way, but the proposal should have a clear statement on this point.

···

On Jun 12, 2016, at 6:46 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding
Proposal: TBD
Author: Brent Royal-Gordon <https://github.com/brentdax>
Status: TBD
Review manager: TBD
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>Introduction

This proposal enhances optional binding with a new, tuple-based syntax for binding multiple values. It replaces functionality lost in SE-0099 with a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099 Restructuring Condition Clauses <http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }
This doesn't conflict with the clause separation syntax because the commas are within parentheses. However, the analogous syntax for optional bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
// error: initializer for conditional binding must have
// Optional type, not '(Int?, Int?, Int?)' (aka
// '(Optional<Int>, Optional<Int>, Optional<Int>)')
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed Solution

We should extend optional binding clauses to permit a tuple of optional values on the right of the = and a tuple of constants with identical arity on the left. Swift should test each element of the tuple on the right, and if none of them are nil, bind them to the constants on the left.

Nothing in this proposal should change the way optional binding handles an optional tuple (T, U)?, as opposed to a tuple of optionals (T?, U?). Even an optional tuple of optionals (T?, U?)? should continue to be handled as before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed Design

No change to the formal grammar is necessary, as the pattern and initializer productions in the optional-binding-head rule can already match tuples:

optional-binding-head : 'let' pattern initializer
Rather, Sema should be modified to detect this situation and generate appropriate code. Currently, TypeCheckPattern.cpp essentially converts let a = opt1 into case let a? = opt1; if this proposal is accepted, it should similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) = (opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge cases

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented by pattern matching, but is a niche feature. It should be supported if easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions returning tuples of optionals

Ideally, optional bindings whose initializer is an expression evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)
if let (a, b) = tuple { ... }
However, I'm not sure if Swift will have pinned down the type of the initializer at the point where it's generating the pattern. If this would be difficult or impossible to implement, Swift should continue to interpret code like this as attempting to bind an optional tuple, rather than a tuple of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name patterns

In theory, Swift could allow you to bind a tuple of optionals to a single constant:

if let tuple = (opt1, opt2) { ... }
However, this seems error-prone; the pattern doesn't draw a very clear picture of the value being operated upon, so you could easily misinterpret it as binding an optional tuple. Because of this ambiguity, Swift should always interpret this construct as binding an optional tuple, rejecting it with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact on Existing Code

This proposal is additive compared to SE-0099, but in combination with it, essentially replaces the Swift 2.2 compound binding syntax with a different, incompatible one. When moving directly from Swift 2.2, the migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }
Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
The "one-let-per-binding" syntax remains compatible with both Swift 2.2 and Swift 3, so projects which must support both can still perform multiple optional bindings in a single if statement without resorting to an #if swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives Considered

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing similar standard library functionality

Rather than including this functionality in the compiler, the standard library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }
public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }
// etc.
These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }
However, because we do not have variadic generics, we would need to provide a set of overloads for different arities, and our support would be limited to the arities we chose to provide. (Support for tuples, as opposed to separate parameters, would require a second set of overloads). Meanwhile, the tuple matching syntax is already precedented in case conditionals, so extending it seems pretty natural. Providing this in the compiler seems like the right solution.

--
Brent Royal-Gordon
Architechies

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


(Haravikk) #5

+1 from me; I never considered that I could do this using pattern matching of a tuple, and as Brent says this is a nicer shorthand for introducing users to conditional binding in advance of teaching the finer details of pattern matching, plus I prefer it visually.

···

On 12 Jun 2016, at 12:46, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding
Proposal: TBD
Author: Brent Royal-Gordon <https://github.com/brentdax>
Status: TBD
Review manager: TBD
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>Introduction

This proposal enhances optional binding with a new, tuple-based syntax for binding multiple values. It replaces functionality lost in SE-0099 with a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099 Restructuring Condition Clauses <http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }
This doesn't conflict with the clause separation syntax because the commas are within parentheses. However, the analogous syntax for optional bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
// error: initializer for conditional binding must have
// Optional type, not '(Int?, Int?, Int?)' (aka
// '(Optional<Int>, Optional<Int>, Optional<Int>)')
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed Solution

We should extend optional binding clauses to permit a tuple of optional values on the right of the = and a tuple of constants with identical arity on the left. Swift should test each element of the tuple on the right, and if none of them are nil, bind them to the constants on the left.

Nothing in this proposal should change the way optional binding handles an optional tuple (T, U)?, as opposed to a tuple of optionals (T?, U?). Even an optional tuple of optionals (T?, U?)? should continue to be handled as before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed Design

No change to the formal grammar is necessary, as the pattern and initializer productions in the optional-binding-head rule can already match tuples:

optional-binding-head : 'let' pattern initializer
Rather, Sema should be modified to detect this situation and generate appropriate code. Currently, TypeCheckPattern.cpp essentially converts let a = opt1 into case let a? = opt1; if this proposal is accepted, it should similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) = (opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge cases

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented by pattern matching, but is a niche feature. It should be supported if easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions returning tuples of optionals

Ideally, optional bindings whose initializer is an expression evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)
if let (a, b) = tuple { ... }
However, I'm not sure if Swift will have pinned down the type of the initializer at the point where it's generating the pattern. If this would be difficult or impossible to implement, Swift should continue to interpret code like this as attempting to bind an optional tuple, rather than a tuple of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name patterns

In theory, Swift could allow you to bind a tuple of optionals to a single constant:

if let tuple = (opt1, opt2) { ... }
However, this seems error-prone; the pattern doesn't draw a very clear picture of the value being operated upon, so you could easily misinterpret it as binding an optional tuple. Because of this ambiguity, Swift should always interpret this construct as binding an optional tuple, rejecting it with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact on Existing Code

This proposal is additive compared to SE-0099, but in combination with it, essentially replaces the Swift 2.2 compound binding syntax with a different, incompatible one. When moving directly from Swift 2.2, the migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }
Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
The "one-let-per-binding" syntax remains compatible with both Swift 2.2 and Swift 3, so projects which must support both can still perform multiple optional bindings in a single if statement without resorting to an #if swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives Considered

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing similar standard library functionality

Rather than including this functionality in the compiler, the standard library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }
public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }
// etc.
These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }
However, because we do not have variadic generics, we would need to provide a set of overloads for different arities, and our support would be limited to the arities we chose to provide. (Support for tuples, as opposed to separate parameters, would require a second set of overloads). Meanwhile, the tuple matching syntax is already precedented in case conditionals, so extending it seems pretty natural. Providing this in the compiler seems like the right solution.

--
Brent Royal-Gordon
Architechies

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


(Chris Lattner) #6

When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.

Hi Brent,

I’m sorry that I haven’t had time yet to read the down thread responses, but I don’t see how this can work:

“if let pattern = expr” is sugar for “if case let pattern? = expr”, so this would either:

a) only apply to tuple literals on the right side or
b) not work, since this syntax above already means “if case let (a,b,c)? = expr”.

If you pick a, then it is makes tuple literals inconsistent with other expressions, which seems to make the language more confusing and less orthogonal.

-Chris

···

On Jun 12, 2016, at 4:46 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

Tuple-Based Compound Optional Binding
Proposal: TBD
Author: Brent Royal-Gordon <https://github.com/brentdax>
Status: TBD
Review manager: TBD
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>Introduction

This proposal enhances optional binding with a new, tuple-based syntax for binding multiple values. It replaces functionality lost in SE-0099 with a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099 Restructuring Condition Clauses <http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }
This doesn't conflict with the clause separation syntax because the commas are within parentheses. However, the analogous syntax for optional bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
// error: initializer for conditional binding must have
// Optional type, not '(Int?, Int?, Int?)' (aka
// '(Optional<Int>, Optional<Int>, Optional<Int>)')
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed Solution

We should extend optional binding clauses to permit a tuple of optional values on the right of the = and a tuple of constants with identical arity on the left. Swift should test each element of the tuple on the right, and if none of them are nil, bind them to the constants on the left.

Nothing in this proposal should change the way optional binding handles an optional tuple (T, U)?, as opposed to a tuple of optionals (T?, U?). Even an optional tuple of optionals (T?, U?)? should continue to be handled as before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed Design

No change to the formal grammar is necessary, as the pattern and initializer productions in the optional-binding-head rule can already match tuples:

optional-binding-head : 'let' pattern initializer
Rather, Sema should be modified to detect this situation and generate appropriate code. Currently, TypeCheckPattern.cpp essentially converts let a = opt1 into case let a? = opt1; if this proposal is accepted, it should similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) = (opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge cases

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented by pattern matching, but is a niche feature. It should be supported if easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions returning tuples of optionals

Ideally, optional bindings whose initializer is an expression evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)
if let (a, b) = tuple { ... }
However, I'm not sure if Swift will have pinned down the type of the initializer at the point where it's generating the pattern. If this would be difficult or impossible to implement, Swift should continue to interpret code like this as attempting to bind an optional tuple, rather than a tuple of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name patterns

In theory, Swift could allow you to bind a tuple of optionals to a single constant:

if let tuple = (opt1, opt2) { ... }
However, this seems error-prone; the pattern doesn't draw a very clear picture of the value being operated upon, so you could easily misinterpret it as binding an optional tuple. Because of this ambiguity, Swift should always interpret this construct as binding an optional tuple, rejecting it with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact on Existing Code

This proposal is additive compared to SE-0099, but in combination with it, essentially replaces the Swift 2.2 compound binding syntax with a different, incompatible one. When moving directly from Swift 2.2, the migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }
Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
The "one-let-per-binding" syntax remains compatible with both Swift 2.2 and Swift 3, so projects which must support both can still perform multiple optional bindings in a single if statement without resorting to an #if swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives Considered

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing similar standard library functionality

Rather than including this functionality in the compiler, the standard library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }
public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }
// etc.
These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }
However, because we do not have variadic generics, we would need to provide a set of overloads for different arities, and our support would be limited to the arities we chose to provide. (Support for tuples, as opposed to separate parameters, would require a second set of overloads). Meanwhile, the tuple matching syntax is already precedented in case conditionals, so extending it seems pretty natural. Providing this in the compiler seems like the right solution.

--
Brent Royal-Gordon
Architechies


(Brent Royal-Gordon) #7

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

You mention `guard case` in the motivation, but I think for the uninitiated reader it would be fair to point out that the following example already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

It seems fair to mention that, so I've added it to the "Alternatives Considered" entry for doing nothing.
Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

The same functionality is also available through case conditions:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }
However, all of optional binding is redundant with case conditions; we keep it anyway because it's a convenient shorthand and saves beginners from having to learn about pattern matching. Multiple bindings are a natural fit for the subset of case features available through optional binding.

···

--
Brent Royal-Gordon
Architechies


(L Mihalkovic) #8

It takes reaching the last section of the proposal to understand that there is not real issue, that it does not add any functionality, and that it basically amount to stylistic preference for something. It might be more informative to start with that rather than leave it until the end.

···

On Jun 12, 2016, at 8:04 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Sun, Jun 12, 2016 at 6:46 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding
Proposal: TBD
Author: Brent Royal-Gordon
Status: TBD
Review manager: TBD
Introduction

This proposal enhances optional binding with a new, tuple-based syntax for binding multiple values. It replaces functionality lost in SE-0099 with a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099 Restructuring Condition Clauses

Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }
This doesn't conflict with the clause separation syntax because the commas are within parentheses. However, the analogous syntax for optional bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
// error: initializer for conditional binding must have
// Optional type, not '(Int?, Int?, Int?)' (aka
// '(Optional<Int>, Optional<Int>, Optional<Int>)')
Proposed Solution

We should extend optional binding clauses to permit a tuple of optional values on the right of the = and a tuple of constants with identical arity on the left. Swift should test each element of the tuple on the right, and if none of them are nil, bind them to the constants on the left.

Nothing in this proposal should change the way optional binding handles an optional tuple (T, U)?, as opposed to a tuple of optionals (T?, U?). Even an optional tuple of optionals (T?, U?)? should continue to be handled as before.

Detailed Design

No change to the formal grammar is necessary, as the pattern and initializer productions in the optional-binding-head rule can already match tuples:

optional-binding-head : 'let' pattern initializer
Rather, Sema should be modified to detect this situation and generate appropriate code. Currently, TypeCheckPattern.cpp essentially converts let a = opt1 into case let a? = opt1; if this proposal is accepted, it should similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) = (opt1, opt2).

Edge cases

Nested tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented by pattern matching, but is a niche feature. It should be supported if easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }
Expressions returning tuples of optionals

Ideally, optional bindings whose initializer is an expression evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)
if let (a, b) = tuple { ... }
However, I'm not sure if Swift will have pinned down the type of the initializer at the point where it's generating the pattern. If this would be difficult or impossible to implement, Swift should continue to interpret code like this as attempting to bind an optional tuple, rather than a tuple of optionals.

I think this proposal is definitely worthy of consideration (and implementation). I hesitate that what works or not is being proposed partly on the basis of whether it's difficult to implement or not. If I could offer a suggestion as to exactly which edge cases should work, I think it should be based on this guideline that can be articulated cleanly:

Given `if let (a, b, c)...` or any other variation or edge case, transform in your mind's eye to `if case let (a?, b?, c?)...`. That is, append `case` before `let` and then append `?` immediately after each constant so introduced. If the `if case let...` version would work, so should the `if let...` version. By that rule of thumb, `if case let (a?, (b?, c?)) = (opt1, (opt2, opt3))` would work if I'm not mistaken, as would `if case let (a?, b?) = tuple`, but `if case let tuple? = (opt1, opt2)` would not and thus neither should `if let tuple = (opt1, opt2)`.

Single-name patterns

In theory, Swift could allow you to bind a tuple of optionals to a single constant:

if let tuple = (opt1, opt2) { ... }
However, this seems error-prone; the pattern doesn't draw a very clear picture of the value being operated upon, so you could easily misinterpret it as binding an optional tuple. Because of this ambiguity, Swift should always interpret this construct as binding an optional tuple, rejecting it with a type error if necessary.

Impact on Existing Code

This proposal is additive compared to SE-0099, but in combination with it, essentially replaces the Swift 2.2 compound binding syntax with a different, incompatible one. When moving directly from Swift 2.2, the migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }
Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
The "one-let-per-binding" syntax remains compatible with both Swift 2.2 and Swift 3, so projects which must support both can still perform multiple optional bindings in a single if statement without resorting to an #if swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
Alternatives Considered

Not accepting this proposal

This proposal does not add new functionality; it merely removes keyword clutter. However, it offers a convenient replacement for a commonly-used feature which has just been removed as a result of grammatical ambiguity, not user confusion or lack of utility.

Providing similar standard library functionality

Rather than including this functionality in the compiler, the standard library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }
public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }
// etc.
These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }
However, because we do not have variadic generics, we would need to provide a set of overloads for different arities, and our support would be limited to the arities we chose to provide. (Support for tuples, as opposed to separate parameters, would require a second set of overloads). Meanwhile, the tuple matching syntax is already precedented in case conditionals, so extending it seems pretty natural. Providing this in the compiler seems like the right solution.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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


(Xiaodi Wu) #9

What's the behavior currently with `if case let (foo?, bar?, baz?)...`?

···

On Sun, Jun 12, 2016 at 19:53 plx via swift-evolution < swift-evolution@swift.org> wrote:

This proposal should specify if the tuple is evaluated “eagerly” or as an
“early-exit”.

That is, if we write `if let (foo,bar,baz) = (_foo(), _bar(), _baz())`,
and _bar() -> nil, will `_baz()` have been evaluated, or not?

I’d use this feature either way, but the proposal should have a clear
statement on this point.

On Jun 12, 2016, at 6:46 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

When I suggested this syntax in the acceptance thread for SE-0099, Chris
said it should be written up as a proposal. I'm sure this will get lost in
the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding

   - Proposal: TBD
   - Author: Brent Royal-Gordon <https://github.com/brentdax>
   - Status: TBD
   - Review manager: TBD

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>
Introduction

This proposal enhances optional binding with a new, tuple-based syntax for
binding multiple values. It replaces functionality lost in SE-0099 with a
syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099
Restructuring Condition Clauses
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>
Motivation

In Swift 2, it was possible to bind multiple optional values in a single if
let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }

SE-0099
<https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified
the syntax of conditional statements, but removed this feature so that , could
instead separate different conditional clauses. Code like this must now use
three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

The similar case clause sidesteps this problem because it can
pattern-match tuples. Hence, you can put several patterns in a tuple on the
left side of the =, and a matching number of values in a tuple on the
right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }

This doesn't conflict with the clause separation syntax because the commas
are within parentheses. However, the analogous syntax for optional bindings
is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }// error: initializer for conditional binding must have // Optional type, not '(Int?, Int?, Int?)' (aka // '(Optional<Int>, Optional<Int>, Optional<Int>)')

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed
Solution

We should extend optional binding clauses to permit a tuple of optional
values on the right of the = and a tuple of constants with identical
arity on the left. Swift should test each element of the tuple on the
right, and if none of them are nil, bind them to the constants on the
left.

Nothing in this proposal should change the way optional binding handles an *optional
tuple* (T, U)?, as opposed to a *tuple of optionals* (T?, U?). Even an
optional tuple of optionals (T?, U?)? should continue to be handled as
before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed
Design

No change to the formal grammar is necessary, as the *pattern* and
*initializer* productions in the *optional-binding-head* rule can already
match tuples:

optional-binding-head : 'let' pattern initializer

Rather, Sema should be modified to detect this situation and generate
appropriate code. Currently, TypeCheckPattern.cpp essentially converts let
a = opt1 into case let a? = opt1; if this proposal is accepted, it should
similarly convert let (a, b) = (opt1, opt2) into case let (a?, b?) =
(opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge
cases
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested
tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented
by pattern matching, but is a niche feature. It should be supported if
easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions
returning tuples of optionals

Ideally, optional bindings whose *initializer* is an expression
evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)if let (a, b) = tuple { ... }

However, I'm not sure if Swift will have pinned down the type of the
initializer at the point where it's generating the pattern. If this would
be difficult or impossible to implement, Swift should continue to interpret
code like this as attempting to bind an optional tuple, rather than a tuple
of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name
patterns

In theory, Swift could allow you to bind a tuple of optionals to a single
constant:

if let tuple = (opt1, opt2) { ... }

However, this seems error-prone; the pattern doesn't draw a very clear
picture of the value being operated upon, so you could easily misinterpret
it as binding an optional tuple. Because of this ambiguity, Swift should
always interpret this construct as binding an optional tuple, rejecting it
with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact
on Existing Code

This proposal is additive compared to SE-0099, but in combination with it,
essentially replaces the Swift 2.2 compound binding syntax with a
different, incompatible one. When moving directly from Swift 2.2, the
migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }

Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

The "one-let-per-binding" syntax remains compatible with both Swift 2.2
and Swift 3, so projects which must support both can still perform multiple
optional bindings in a single if statement without resorting to an #if
swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives
Considered
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not
accepting this proposal

This proposal does not add new functionality; it merely removes keyword
clutter. However, it offers a convenient replacement for a commonly-used
feature which has just been removed as a result of grammatical ambiguity,
not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing
similar standard library functionality

Rather than including this functionality in the compiler, the standard
library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }// etc.

These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }

However, because we do not have variadic generics, we would need to
provide a set of overloads for different arities, and our support would be
limited to the arities we chose to provide. (Support for tuples, as opposed
to separate parameters, would require a second set of overloads).
Meanwhile, the tuple matching syntax is already precedented in case conditionals,
so extending it seems pretty natural. Providing this in the compiler seems
like the right solution.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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


(Patrick Smith) #10

If Pyry’s suggestion remained the preferred way of unwrapping a tuple, could it also become the only way for unwrapping a single item?

guard case let a? = opt1 {...}
Or even shortened for matching optionals only:

guard let a? = opt1 {...}
Or even as has often been requested, to keep the same name:
guard let opt1? {...}
Multiples:
guard let (opt1?, opt2?, opt3?) {...}
guard let (a?, b?, c?) = (opt1, opt2, opt3) {...}
Sorry, not trying to derail, but it always has seemed like something shorter and more self explanatory could be made for optionals. `?` in pattern matching is a special syntax anyway, so why not make this common use case easier?

Patrick

···

_____________________________
From: Pyry Jahkola via swift-evolution <swift-evolution@swift.org>
Sent: Sunday, June 12, 2016 10:04 PM
Subject: Re: [swift-evolution] [Draft] Tuple-Based Compound Optional Binding
To: Brent Royal-Gordon <brent@architechies.com>
Cc: swift-evolution List <swift-evolution@swift.org>

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
You mention `guard case` in the motivation, but I think for the uninitiated reader it would be fair to point out that the following example already works equivalently, with only a few extra characters:
guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }
Aside of that, it's yet more magic to our `if let` syntax but I don't mind, it would be useful at times.
— Pyry


(Chris Lattner) #11

Sorry, I mean that:

"if let (a, b, c) = expr”
already means:
“if case let (a,b,c)? = expr”

since “expr” can be an optional tuple.

-Chris

···

On Jun 21, 2016, at 11:11 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 12, 2016, at 4:46 AM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

When I suggested this syntax in the acceptance thread for SE-0099, Chris said it should be written up as a proposal. I'm sure this will get lost in the WWDC shuffle, but here goes.

Hi Brent,

I’m sorry that I haven’t had time yet to read the down thread responses, but I don’t see how this can work:

“if let pattern = expr” is sugar for “if case let pattern? = expr”, so this would either:

a) only apply to tuple literals on the right side or
b) not work, since this syntax above already means “if case let (a,b,c)? = expr”.


(Brent Royal-Gordon) #12

It takes reaching the last section of the proposal to understand that there is not real issue, that it does not add any functionality, and that it basically amount to stylistic preference for something. It might be more informative to start with that rather than leave it until the end.

You're right; it's quite fair to say that acknowledgement should be moved up front. At the same time, I can better justify why it's not a good solution to the problem.

Motivation

In Swift 2, it was possible to bind multiple optional values in a single if let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }
SE-0099 <https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified the syntax of conditional statements, but removed this feature so that , could instead separate different conditional clauses. Code like this must now use three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }
The similar case clause sidesteps this problem because it can pattern-match tuples. Hence, you can put several patterns in a tuple on the left side of the =, and a matching number of values in a tuple on the right side, and match them all with one case clause. This doesn't conflict with the clause separation syntax because the commas are within parentheses.

In a pinch, this can be used to pattern-match against several optionals:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }
However, optional binding is itself redundant in exactly the same way. Optional binding exists because the pattern-matching syntax is too clumsy, and requires too much understanding from the user, for such a common operation. The same concerns apply when performing multiple optional bindings.

Swift's syntax actually permits you to write an optional binding clause analogous to the tuple pattern-match above:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }
However, Swift does not interpret this as an attempt to bind several optionals inside a tuple; instead it assumes you want to bind and destructure an optional tuple, and type-checking fails because the tuple is not optional.

error: initializer for conditional binding must have Optional type, not
'(Int?, Int?, Int?)' (aka '(Optional<Int>, Optional<Int>, Optional<Int>)')

···

--
Brent Royal-Gordon
Architechies


(Johan K. Jensen) #13

Currently `if case let (foo?, bar?, baz?) = (_foo(), _bar(), _baz())`, and
_bar() returns nil, then `_baz()` will not be evaluated.

···

On Mon, Jun 13, 2016 at 2:54 AM, Xiaodi Wu via swift-evolution < swift-evolution@swift.org> wrote:

What's the behavior currently with `if case let (foo?, bar?, baz?)...`?

On Sun, Jun 12, 2016 at 19:53 plx via swift-evolution < > swift-evolution@swift.org> wrote:

This proposal should specify if the tuple is evaluated “eagerly” or as an
“early-exit”.

That is, if we write `if let (foo,bar,baz) = (_foo(), _bar(), _baz())`,
and _bar() -> nil, will `_baz()` have been evaluated, or not?

I’d use this feature either way, but the proposal should have a clear
statement on this point.

On Jun 12, 2016, at 6:46 AM, Brent Royal-Gordon via swift-evolution < >> swift-evolution@swift.org> wrote:

When I suggested this syntax in the acceptance thread for SE-0099, Chris
said it should be written up as a proposal. I'm sure this will get lost in
the WWDC shuffle, but here goes.
Tuple-Based Compound Optional Binding

   - Proposal: TBD
   - Author: Brent Royal-Gordon <https://github.com/brentdax>
   - Status: TBD
   - Review manager: TBD

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#introduction>
Introduction

This proposal enhances optional binding with a new, tuple-based syntax
for binding multiple values. It replaces functionality lost in SE-0099 with
a syntax compatible with the new design.

Swift Evolution Discussion: [Accepted with Revision] SE-0099
Restructuring Condition Clauses
<http://thread.gmane.org/gmane.comp.lang.swift.evolution/19452/focus=20139>

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#motivation>
Motivation

In Swift 2, it was possible to bind multiple optional values in a single if
let, guard let, or while let clause:

guard let a = opt1, b = opt2, c = opt3 else { ... }

SE-0099
<https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md> simplified
the syntax of conditional statements, but removed this feature so that , could
instead separate different conditional clauses. Code like this must now use
three separate optional binding clauses:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

The similar case clause sidesteps this problem because it can
pattern-match tuples. Hence, you can put several patterns in a tuple on the
left side of the =, and a matching number of values in a tuple on the
right side, and match them all with one case clause:

guard case (.none, .none, .none) = (opt1, opt2, opt3) else { ... }

This doesn't conflict with the clause separation syntax because the
commas are within parentheses. However, the analogous syntax for optional
bindings is not permitted:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }// error: initializer for conditional binding must have // Optional type, not '(Int?, Int?, Int?)' (aka // '(Optional<Int>, Optional<Int>, Optional<Int>)')

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#proposed-solution>Proposed
Solution

We should extend optional binding clauses to permit a tuple of optional
values on the right of the = and a tuple of constants with identical
arity on the left. Swift should test each element of the tuple on the
right, and if none of them are nil, bind them to the constants on the
left.

Nothing in this proposal should change the way optional binding handles
an *optional tuple* (T, U)?, as opposed to a *tuple of optionals* (T?,
U?). Even an optional tuple of optionals (T?, U?)? should continue to be
handled as before.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#detailed-design>Detailed
Design

No change to the formal grammar is necessary, as the *pattern* and
*initializer* productions in the *optional-binding-head* rule can
already match tuples:

optional-binding-head : 'let' pattern initializer

Rather, Sema should be modified to detect this situation and generate
appropriate code. Currently, TypeCheckPattern.cpp essentially converts let
a = opt1 into case let a? = opt1; if this proposal is accepted, it
should similarly convert let (a, b) = (opt1, opt2) into case let (a?,
b?) = (opt1, opt2).

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#edge-cases>Edge
cases
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#nested-tuples-of-optionals>Nested
tuples-of-optionals

Permitting deeper pattern matching of nested tuples is highly precedented
by pattern matching, but is a niche feature. It should be supported if
easily achievable.

guard let (a, (b, c)) = (opt1, (opt2, opt3)) else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#expressions-returning-tuples-of-optionals>Expressions
returning tuples of optionals

Ideally, optional bindings whose *initializer* is an expression
evaluating to a tuple of optionals would be supported:

let tuple = (opt1, opt2)if let (a, b) = tuple { ... }

However, I'm not sure if Swift will have pinned down the type of the
initializer at the point where it's generating the pattern. If this would
be difficult or impossible to implement, Swift should continue to interpret
code like this as attempting to bind an optional tuple, rather than a tuple
of optionals.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#single-name-patterns>Single-name
patterns

In theory, Swift could allow you to bind a tuple of optionals to a single
constant:

if let tuple = (opt1, opt2) { ... }

However, this seems error-prone; the pattern doesn't draw a very clear
picture of the value being operated upon, so you could easily misinterpret
it as binding an optional tuple. Because of this ambiguity, Swift should
always interpret this construct as binding an optional tuple, rejecting it
with a type error if necessary.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#impact-on-existing-code>Impact
on Existing Code

This proposal is additive compared to SE-0099, but in combination with
it, essentially replaces the Swift 2.2 compound binding syntax with a
different, incompatible one. When moving directly from Swift 2.2, the
migrator should convert old-style compound optional binding clauses:

guard let a = opt1, b = opt2, c = opt3 else { ... }

Into the new, tuple-based ones:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

The "one-let-per-binding" syntax remains compatible with both Swift 2.2
and Swift 3, so projects which must support both can still perform multiple
optional bindings in a single if statement without resorting to an #if
swift(>=3.0) build configuration:

guard let a = opt1, let b = opt2, let c = opt3 else { ... }

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#alternatives-considered>Alternatives
Considered
<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#not-accepting-this-proposal>Not
accepting this proposal

This proposal does not add new functionality; it merely removes keyword
clutter. However, it offers a convenient replacement for a commonly-used
feature which has just been removed as a result of grammatical ambiguity,
not user confusion or lack of utility.

<https://gist.github.com/brentdax/46c340c967358589ade5351531ac8920#providing-similar-standard-library-functionality>Providing
similar standard library functionality

Rather than including this functionality in the compiler, the standard
library could provide a series of functions like:

public func all<T, U>(_ t: T?, _ u: U?) -> (T, U)? { ... }public func all<T, U, V>(_ t: T?, _ u: U?, _ v: V?) -> (T, U, V)? { ... }// etc.

These could then be used in a similar fashion to this proposal:

guard let (a, b, c) = all(opt1, opt2, opt3) else { ... }

However, because we do not have variadic generics, we would need to
provide a set of overloads for different arities, and our support would be
limited to the arities we chose to provide. (Support for tuples, as opposed
to separate parameters, would require a second set of overloads).
Meanwhile, the tuple matching syntax is already precedented in case conditionals,
so extending it seems pretty natural. Providing this in the compiler seems
like the right solution.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #14

This has been suggested before, I believe. The core team has weighed in
several times; it seemed like there was some disagreement amongst them
whether the current syntax is the wisest, but the concluding statement
seemed uncontroversial: "I don't think it's something we can change."

Source:
http://article.gmane.org/gmane.comp.lang.swift.evolution/15879/

···

On Mon, Jun 13, 2016 at 10:32 PM, Patrick Smith via swift-evolution < swift-evolution@swift.org> wrote:

If Pyry’s suggestion remained the preferred way of unwrapping a tuple,
could it also become the only way for unwrapping a single item?

guard case let a? = opt1 {
...
}

Or even shortened for matching optionals only:

guard let a? = opt1 {
...
}

Or even as has often been requested, to keep the same name:

guard let opt1? {
...
}

Multiples:

guard let (opt1?, opt2?, opt3?) {
...
}

guard let (a?, b?, c?) = (opt1, opt2, opt3) {
...
}

Sorry, not trying to derail, but it always has seemed like something
shorter and more self explanatory could be made for optionals. `?` in
pattern matching is a special syntax anyway, so why not make this common
use case easier?

Patrick

_____________________________
From: Pyry Jahkola via swift-evolution <swift-evolution@swift.org>
Sent: Sunday, June 12, 2016 10:04 PM
Subject: Re: [swift-evolution] [Draft] Tuple-Based Compound Optional
Binding
To: Brent Royal-Gordon <brent@architechies.com>
Cc: swift-evolution List <swift-evolution@swift.org>

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

You mention `guard case` in the motivation, but I think for the
uninitiated reader it would be fair to point out that the following example
already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

Aside of that, it's yet more magic to our `if let` syntax but I don't
mind, it would be useful at times.

— Pyry

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


(Xiaodi Wu) #15

Also, see:
https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md

···

On Mon, Jun 13, 2016 at 11:06 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

This has been suggested before, I believe. The core team has weighed in
several times; it seemed like there was some disagreement amongst them
whether the current syntax is the wisest, but the concluding statement
seemed uncontroversial: "I don't think it's something we can change."

Source:
http://article.gmane.org/gmane.comp.lang.swift.evolution/15879/

On Mon, Jun 13, 2016 at 10:32 PM, Patrick Smith via swift-evolution < > swift-evolution@swift.org> wrote:

If Pyry’s suggestion remained the preferred way of unwrapping a tuple,
could it also become the only way for unwrapping a single item?

guard case let a? = opt1 {
...
}

Or even shortened for matching optionals only:

guard let a? = opt1 {
...
}

Or even as has often been requested, to keep the same name:

guard let opt1? {
...
}

Multiples:

guard let (opt1?, opt2?, opt3?) {
...
}

guard let (a?, b?, c?) = (opt1, opt2, opt3) {
...
}

Sorry, not trying to derail, but it always has seemed like something
shorter and more self explanatory could be made for optionals. `?` in
pattern matching is a special syntax anyway, so why not make this common
use case easier?

Patrick

_____________________________
From: Pyry Jahkola via swift-evolution <swift-evolution@swift.org>
Sent: Sunday, June 12, 2016 10:04 PM
Subject: Re: [swift-evolution] [Draft] Tuple-Based Compound Optional
Binding
To: Brent Royal-Gordon <brent@architechies.com>
Cc: swift-evolution List <swift-evolution@swift.org>

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution < >> swift-evolution@swift.org> wrote:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

You mention `guard case` in the motivation, but I think for the
uninitiated reader it would be fair to point out that the following example
already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

Aside of that, it's yet more magic to our `if let` syntax but I don't
mind, it would be useful at times.

— Pyry

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


(Patrick Smith) #16

Thanks Xiaodi. Interesting arguments there. It possibly seems a shame to me, because it has knock on effects of making other things more complicated. But I do see how for the most simple case of unwrapping a single Optional, it makes sense.

As much as I would like Brent’s proposal to make things easier to type, I think nesting things inside a tuple, where a reader must keep track of which input matches which output, could lead to harder to follow code.

In my own code I will probably stick to using the long form `guard let a = opt1, let b = opt2, let c = opt3 else { … }`, not because I would like typing it, but because it will be the most clear.

I would have loved for SE-0099 to have gone with semicolon/newline separators so I could write it like this, but alas:

guard
  let a = opt1
  let b = opt2
  let c = opt3
else {
  …
}

A different thought — maybe if Xcode had autocompletion to unwrap Optionals when inside an `if` or `guard` statement it would make things a lot easier. Just type the full name of the optional, then press escape, and then code will be suggested to unwrap it. Is the pain this proposal targets mainly in the writing more than reading?

guard opt·|·

guard let <#placeholder#> = opt1

guard let a = opt1

guard let a = opt1, opt2·|·

guard let a = opt1, let <#placeholder#> = opt2

guard let a = opt1, let b = opt2

Patrick

···

On 14 Jun 2016, at 2:07 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Also, see: https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md

On Mon, Jun 13, 2016 at 11:06 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
This has been suggested before, I believe. The core team has weighed in several times; it seemed like there was some disagreement amongst them whether the current syntax is the wisest, but the concluding statement seemed uncontroversial: "I don't think it's something we can change."

Source:
http://article.gmane.org/gmane.comp.lang.swift.evolution/15879/

On Mon, Jun 13, 2016 at 10:32 PM, Patrick Smith via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
If Pyry’s suggestion remained the preferred way of unwrapping a tuple, could it also become the only way for unwrapping a single item?

guard case let a? = opt1 {
...
}

Or even shortened for matching optionals only:

guard let a? = opt1 {
...
}

Or even as has often been requested, to keep the same name:

guard let opt1? {
...
}

Multiples:

guard let (opt1?, opt2?, opt3?) {
...
}

guard let (a?, b?, c?) = (opt1, opt2, opt3) {
...
}

Sorry, not trying to derail, but it always has seemed like something shorter and more self explanatory could be made for optionals. `?` in pattern matching is a special syntax anyway, so why not make this common use case easier?

Patrick

_____________________________
From: Pyry Jahkola via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
Sent: Sunday, June 12, 2016 10:04 PM
Subject: Re: [swift-evolution] [Draft] Tuple-Based Compound Optional Binding
To: Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>>
Cc: swift-evolution List <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

You mention `guard case` in the motivation, but I think for the uninitiated reader it would be fair to point out that the following example already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

Aside of that, it's yet more magic to our `if let` syntax but I don't mind, it would be useful at times.

— Pyry

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


(L Mihalkovic) #17

Thanks Xiaodi. Interesting arguments there. It possibly seems a shame to me, because it has knock on effects of making other things more complicated. But I do see how for the most simple case of unwrapping a single Optional, it makes sense.

As much as I would like Brent’s proposal to make things easier to type, I think nesting things inside a tuple, where a reader must keep track of which input matches which output, could lead to harder to follow code.

Isomehow I think last yesterday's keynote should recast some expectations about the degree of complexity (richness) the language will ever reach... Somehow xamarin/c# might endupmbeing swift++ for many people

···

On Jun 14, 2016, at 11:31 AM, Patrick Smith via swift-evolution <swift-evolution@swift.org> wrote:

In my own code I will probably stick to using the long form `guard let a = opt1, let b = opt2, let c = opt3 else { … }`, not because I would like typing it, but because it will be the most clear.

I would have loved for SE-0099 to have gone with semicolon/newline separators so I could write it like this, but alas:

guard
  let a = opt1
  let b = opt2
  let c = opt3
else {
  …
}

A different thought — maybe if Xcode had autocompletion to unwrap Optionals when inside an `if` or `guard` statement it would make things a lot easier. Just type the full name of the optional, then press escape, and then code will be suggested to unwrap it. Is the pain this proposal targets mainly in the writing more than reading?

guard opt·|·

guard let <#placeholder#> = opt1

guard let a = opt1

guard let a = opt1, opt2·|·

guard let a = opt1, let <#placeholder#> = opt2

guard let a = opt1, let b = opt2

Patrick

On 14 Jun 2016, at 2:07 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Also, see: https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md

On Mon, Jun 13, 2016 at 11:06 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
This has been suggested before, I believe. The core team has weighed in several times; it seemed like there was some disagreement amongst them whether the current syntax is the wisest, but the concluding statement seemed uncontroversial: "I don't think it's something we can change."

Source:
http://article.gmane.org/gmane.comp.lang.swift.evolution/15879/

On Mon, Jun 13, 2016 at 10:32 PM, Patrick Smith via swift-evolution <swift-evolution@swift.org> wrote:
If Pyry’s suggestion remained the preferred way of unwrapping a tuple, could it also become the only way for unwrapping a single item?

guard case let a? = opt1 {
...
}

Or even shortened for matching optionals only:

guard let a? = opt1 {
...
}

Or even as has often been requested, to keep the same name:

guard let opt1? {
...
}

Multiples:

guard let (opt1?, opt2?, opt3?) {
...
}

guard let (a?, b?, c?) = (opt1, opt2, opt3) {
...
}

Sorry, not trying to derail, but it always has seemed like something shorter and more self explanatory could be made for optionals. `?` in pattern matching is a special syntax anyway, so why not make this common use case easier?

Patrick

_____________________________
From: Pyry Jahkola via swift-evolution <swift-evolution@swift.org>
Sent: Sunday, June 12, 2016 10:04 PM
Subject: Re: [swift-evolution] [Draft] Tuple-Based Compound Optional Binding
To: Brent Royal-Gordon <brent@architechies.com>
Cc: swift-evolution List <swift-evolution@swift.org>

On 12 Jun 2016, at 14:46, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

guard let (a, b, c) = (opt1, opt2, opt3) else { ... }

You mention `guard case` in the motivation, but I think for the uninitiated reader it would be fair to point out that the following example already works equivalently, with only a few extra characters:

guard case let (a?, b?, c?) = (opt1, opt2, opt3) else { ... }

Aside of that, it's yet more magic to our `if let` syntax but I don't mind, it would be useful at times.

— Pyry

_______________________________________________
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


(Austin Zheng) #18

How so? What proposals might the core team accept that would confirm your suspicions; would this be one of them? Maybe I should drop Swift and move to C#, if that language is going to end up so much better than Swift in the future. It's never good to be tied down to a single language.

Best,
Austin

···

On Jun 14, 2016, at 7:12 AM, L. Mihalkovic via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 14, 2016, at 11:31 AM, Patrick Smith via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Thanks Xiaodi. Interesting arguments there. It possibly seems a shame to me, because it has knock on effects of making other things more complicated. But I do see how for the most simple case of unwrapping a single Optional, it makes sense.

As much as I would like Brent’s proposal to make things easier to type, I think nesting things inside a tuple, where a reader must keep track of which input matches which output, could lead to harder to follow code.

Isomehow I think last yesterday's keynote should recast some expectations about the degree of complexity (richness) the language will ever reach... Somehow xamarin/c# might endupmbeing swift++ for many people


(L Mihalkovic) #19

I think it is non-disputable that objc is a very simple language when compared to more recent languages. Today swift is capable of doing a lot of things, while still being a simpler language than older ones. Si the question for some people might be how much richer will swift become? Will it rival scala's type system? Will it rival java/scala/kotlin/ceylon/c++ for the ability to organize large codebases? Will it have the runtime/compile time code fluidity of D? Etc.. The only way to find out is ... there is none. So then who is swift for? Apple wants it usable by people off the street... not people with a degree in computer science, but the people who may one day get a degree or not. So i wonder this plus the fact that objc was enough for so many years doesn't simply mean that there is already a cap on the sophistication swift will ever get!!! that they will touch everything around it, before they push it. Today i have a degree of expressiveness with c# that i cannot have with swift, is the gap going to increase of decrease? That is what i care to know about before I advise large corps to invest in swift or not. bored/curious devs (i included) will always easily pick it up, but should i advise a CTO to invest on it...

···

On Jun 15, 2016, at 5:04 PM, Austin Zheng <austinzheng@gmail.com> wrote:

On Jun 14, 2016, at 7:12 AM, L. Mihalkovic via swift-evolution <swift-evolution@swift.org> wrote:

On Jun 14, 2016, at 11:31 AM, Patrick Smith via swift-evolution <swift-evolution@swift.org> wrote:

Thanks Xiaodi. Interesting arguments there. It possibly seems a shame to me, because it has knock on effects of making other things more complicated. But I do see how for the most simple case of unwrapping a single Optional, it makes sense.

As much as I would like Brent’s proposal to make things easier to type, I think nesting things inside a tuple, where a reader must keep track of which input matches which output, could lead to harder to follow code.

Isomehow I think last yesterday's keynote should recast some expectations about the degree of complexity (richness) the language will ever reach... Somehow xamarin/c# might endupmbeing swift++ for many people

How so? What proposals might the core team accept that would confirm your suspicions; would this be one of them? Maybe I should drop Swift and move to C#, if that language is going to end up so much better than Swift in the future. It's never good to be tied down to a single language.


(Vladimir) #20

Thanks Xiaodi. Interesting arguments there. It possibly seems a shame
to me, because it has knock on effects of making other things more
complicated. But I do see how for the most simple case of unwrapping a
single Optional, it makes sense.

As much as I would like Brent’s proposal to make things easier to type,
I think nesting things inside a tuple, where a reader must keep track
of which input matches which output, could lead to harder to follow code.

Isomehow I think last yesterday's keynote should recast some
expectations about the degree of complexity (richness) the language will
ever reach... Somehow xamarin/c# might endupmbeing swift++ for many people

How so? What proposals might the core team accept that would confirm your
suspicions; would this be one of them? Maybe I should drop Swift and move
to C#, if that language is going to end up so much better than Swift in
the future. It's never good to be tied down to a single language.

I think it is non-disputable that objc is a very simple language when
compared to more recent languages. Today swift is capable of doing a lot of
things, while still being a simpler language than older ones. Si the
question for some people might be how much richer will swift become? Will
it rival scala's type system? Will it rival java/scala/kotlin/ceylon/c++
for the ability to organize large codebases? Will it have the
runtime/compile time code fluidity of D? Etc.. The only way to find out is
... there is none. So then who is swift for? Apple wants it usable by
people off the street... not people with a degree in computer science, but
the people who may one day get a degree or not. So i wonder this plus the
fact that objc was enough for so many years doesn't simply mean that there
is already a cap on the sophistication swift will ever get!!! that they
will touch everything around it, before they push it. Today i have a degree
of expressiveness with c# that i cannot have with swift, is the gap going
to increase of decrease? That is what i care to know about before I advise
large corps to invest in swift or not. bored/curious devs (i included) will
always easily pick it up, but should i advise a CTO to invest on it...

Very interesting opinion, thank you for sharing your thoughts. I believe many of us(here in mailing list) have the same thoughts and questions.

I'd like to believe that Apple wants to make Swift be very easy and simple on start, but very powerful and feature reach when you(as a developer) grows and when you need "more". So I hope Apple will keep Swift very simple to start using by "people off the street", but at the same time will increase Swift features for 'advanced' programming and keep the language on the level near the other modern languages like C# and will adopt best features/conceptions from other languages.

C# is more mature language but, relating to Apple development, you need to deal with its runtime(Mono) and garbage collection. So there is some drawbacks in using of C# also, as I understand.

···

On 15.06.2016 18:42, L. Mihalkovic via swift-evolution wrote:

On Jun 15, 2016, at 5:04 PM, Austin Zheng <austinzheng@gmail.com > <mailto:austinzheng@gmail.com>> wrote:

On Jun 14, 2016, at 7:12 AM, L. Mihalkovic via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
On Jun 14, 2016, at 11:31 AM, Patrick Smith 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