From ec0fdfda50781bcd577991baf023229b3cc238a7 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 24 Feb 2023 16:21:20 -0800 Subject: [PATCH 01/21] Start roughing in 'if' and 'switch' expressions. --- TSPL.docc/ReferenceManual/Expressions.md | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 6fa8ba59a..95849d146 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -921,6 +921,41 @@ to make use of the implementation in their superclass. > > *superclass-initializer-expression* → **`super`** **`.`** **`init`** +### Conditional Expression + +A *conditional expression* XXX + +A conditional expression can appear only in the following contexts: + +- As the value assigned to a variable. +- As the initial value in a variable or constant declaration. +- As the value returned by a function, closure, or property getter. + + + +> Grammar of a conditional expression: +> +> *if-expression* → **`if`** *condition-list* **`{`** expression **`}`** *if-expression-tail* +> *if-expression-tail* → **`else`** *condition-list* **`{`** expression **`}`** *if-expression-tail* +> *if-expression-tail* → **`else`** *if-expression* +> +> +> +> *switch-expression* → **`switch`** *expression* **`{`** *switch-expression-cases* **`}`** +> +> *switch-expression-cases* → *switch-expression-case* *switch-expression-cases*_?_ +> +> *switch-expression-case* → *case-label* *expression* +> +> *switch-expression-case* → *default-label* *expression* + ### Closure Expression A *closure expression* creates a closure, From 26200f95e12c37126744bf8bfc016b058727ea8e Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 24 Feb 2023 16:27:47 -0800 Subject: [PATCH 02/21] Answer a question by reading the compiler's tests. The test cases that exercise this feature include switches over a Bool with just a true and false case. https://github.com/apple/swift/pull/63378 This just follows the principle from the rest of Swift that switches must be exhaustive -- it's not a special case here -- which is probably why the SE proposal doesn't call it out like it does for 'if' expressions needing an 'else' clause. --- TSPL.docc/ReferenceManual/Expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 95849d146..1b584c353 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -936,7 +936,7 @@ OUTLINE - each branch must be a single expression -- except for branches that throw or trap - each branch must produce a value of the same type -- and must typecheck independently -- an 'if' must have an 'else' -- does a 'switch' have to have a default? +- an 'if' must have an 'else' -- 'switch' must be exhaustive - conditional expressions can't be used inside a result builder --> From a5e63b77a16ef6e6de57e65065a373698e59c953 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 27 Feb 2023 16:32:55 -0800 Subject: [PATCH 03/21] Rough in reference for conditional expression. --- TSPL.docc/ReferenceManual/Expressions.md | 55 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 1b584c353..51c9024d8 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -923,22 +923,53 @@ to make use of the implementation in their superclass. ### Conditional Expression -A *conditional expression* XXX +A *conditional expression* evaluates to one of several given values +based on the value of a condition. +It has one the following forms: + +```swift +if <#condition 1#> { + <#expression used if condition 1 is true#> +} else if <#condition 2#> { + <#expression used if condition 2 is true#> +} else { + <#expression used if both conditions are false#> +} -A conditional expression can appear only in the following contexts: +switch <#expression#> { +case <#pattern 1#>: + <#expression 1#> +case <#pattern 2#> where <#condition#>: + <#expression 2#> +default: + <#expression 3#> +} +``` -- As the value assigned to a variable. -- As the initial value in a variable or constant declaration. -- As the value returned by a function, closure, or property getter. +A conditional expression +has the same behavior and syntax as an if statement or switch statement, +with the following differences: - + - As the value assigned to a variable. + + - As the initial value in a variable or constant declaration. + + - As the value returned by a function, closure, or property getter. + +- Each branch must contain a single expression, + which is used as the value for the conditional expression + when that branch's conditional is true. + + + +- An `if` branch must have a corresponding `else` branch. + +- Each branch must produce a value of the same type. + Type checking of each branch happens independently. + +- A conditional expression can't appear inside a result builder. > Grammar of a conditional expression: > From 3642280f73fb013e833269baec02e4119bd0cc7d Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 27 Feb 2023 17:43:02 -0800 Subject: [PATCH 04/21] Sketch possible examples for the guide. --- TSPL.docc/LanguageGuide/ControlFlow.md | 31 ++++++++++++++++++++++++ TSPL.docc/ReferenceManual/Expressions.md | 11 +++++++++ 2 files changed, 42 insertions(+) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 4cfd7ce17..2b0535ff1 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -1285,6 +1285,37 @@ and `distance` is an integer in both patterns --- which means that the code in the body of the `case` can always access a value for `distance`. + +### If and Switch Expressions + +```swift +let temperatureInCelsius = 25 + +let weatherAdvice = if temperatureInCelsius <= 0 { + "It's very cold. Consider wearing a scarf." +} else if temperatureInCelsius >= 30 { + "It's really warm. Don't forget to wear sunscreen." +} else { + "It's not that cold. Wear a t-shirt." +} + +print(weatherAdvice) +// Prints "It's not that cold. Wear a t-shirt." +``` + +```swift +let yetAnotherPoint = (1, -1) +switch yetAnotherPoint { +case let (x, y) where x == y: + print("(\(x), \(y)) is on the line x == y") +case let (x, y) where x == -y: + print("(\(x), \(y)) is on the line x == -y") +case let (x, y): + print("(\(x), \(y)) is just some arbitrary point") +} +// Prints "(1, -1) is on the line x == -y" +``` + ## Control Transfer Statements *Control transfer statements* change the order in which your code is executed, diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 51c9024d8..74dfd0350 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -969,8 +969,19 @@ with the following differences: - Each branch must produce a value of the same type. Type checking of each branch happens independently. + + - A conditional expression can't appear inside a result builder. + + > Grammar of a conditional expression: > > *if-expression* → **`if`** *condition-list* **`{`** expression **`}`** *if-expression-tail* From 468c9aae843d468c671430059eb443c639fdea01 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 6 Mar 2023 10:24:55 -0800 Subject: [PATCH 05/21] Show the expression form next to statement forms. Today if & switch expressions are interesting because they're new -- but over time they'll be less something novel and more just something that works. Introducing them as part of introducing the corresponding statement form, as an alternate spelling, should age better. --- TSPL.docc/LanguageGuide/ControlFlow.md | 118 ++++++++++++++++++------- 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 2b0535ff1..a5595664c 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -759,6 +759,73 @@ if temperatureInFahrenheit <= 32 { Because the temperature is neither too cold nor too warm to trigger the `if` or `else if` conditions, no message is printed. +Swift provides another version of `if` statements +that you can use to set a value based on certain conditions. +For example, +consider the following code that uses `if` statements: + +``` + +```swift +let temperatureInCelsius = 25 +let weatherAdvice: String + +if temperatureInCelsius <= 0 { + weatherAdvice = "It's very cold. Consider wearing a scarf." +} else if temperatureInCelsius >= 30 { + weatherAdvice = "It's really warm. Don't forget to wear sunscreen." +} else { + weatherAdvice = "It's not that cold. Wear a t-shirt." +} + +print(weatherAdvice) +// Prints "It's not that cold. Wear a t-shirt." +``` + +Here, each of the branches sets a value for the `weatherAdvice` constant, +which is printed after the `if` statement. + + +Using the alternate syntax, +known as an `if` expression, +you could write this code more concisely: + +```swift +let weatherAdvice = if temperatureInCelsius <= 0 { + "It's very cold. Consider wearing a scarf." +} else if temperatureInCelsius >= 30 { + "It's really warm. Don't forget to wear sunscreen." +} else { + "It's not that cold. Wear a t-shirt." +} + +print(weatherAdvice) +// Prints "It's not that cold. Wear a t-shirt." +``` + +In this `if` expression version of the code, +each branch contains a single value. +If that branch's condition is true, +then its value is used as the value for the whole `if` expression +in the assignment of `weatherAdvice`. +Every `if` branch has a corresponding `else if` branch or `else` branch, +ensuring that one of the branches always produces a value, +regardless of which conditions are true. + +Because the syntax for the assignment starts outside the `if` expression, +there's no need to repeat `weatherAdvice =` inside each branch --- +instead, +each branch of the `if` expression +produces of the three possible values for `weatherAdvice`, +and the assignment uses that value. + +You can use `if` expressions +on the right-hand side of an assignment, +as shown in the example above, +and as the value that a function or closure returns. + + + ### Switch A `switch` statement considers a value @@ -844,6 +911,26 @@ this `switch` statement uses a `default` case to match all characters other than `a` and `z`. This provision ensures that the `switch` statement is exhaustive. +Like `if` statements, +`switch` statements also have an expression form: + +```swift +let anotherCharacter: Character = "a" +let message = switch anotherCharacter { +case "a": + "The first letter of the alphabet" +case "z": + "The last letter of the alphabet" +default: + "Some other character" +} + +print(message) +// Prints "The first letter of the alphabet" +``` + + + #### No Implicit Fallthrough In contrast with `switch` statements in C and Objective-C, @@ -1285,37 +1372,6 @@ and `distance` is an integer in both patterns --- which means that the code in the body of the `case` can always access a value for `distance`. - -### If and Switch Expressions - -```swift -let temperatureInCelsius = 25 - -let weatherAdvice = if temperatureInCelsius <= 0 { - "It's very cold. Consider wearing a scarf." -} else if temperatureInCelsius >= 30 { - "It's really warm. Don't forget to wear sunscreen." -} else { - "It's not that cold. Wear a t-shirt." -} - -print(weatherAdvice) -// Prints "It's not that cold. Wear a t-shirt." -``` - -```swift -let yetAnotherPoint = (1, -1) -switch yetAnotherPoint { -case let (x, y) where x == y: - print("(\(x), \(y)) is on the line x == y") -case let (x, y) where x == -y: - print("(\(x), \(y)) is on the line x == -y") -case let (x, y): - print("(\(x), \(y)) is just some arbitrary point") -} -// Prints "(1, -1) is on the line x == -y" -``` - ## Control Transfer Statements *Control transfer statements* change the order in which your code is executed, From 1f41c46fb71cdf343b9c13a9acd279cdf21a5a71 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 6 Mar 2023 12:39:00 -0800 Subject: [PATCH 06/21] Show an if expression in the tour. --- TSPL.docc/GuidedTour/GuidedTour.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/TSPL.docc/GuidedTour/GuidedTour.md b/TSPL.docc/GuidedTour/GuidedTour.md index e310fad2c..a7aa0fcc2 100644 --- a/TSPL.docc/GuidedTour/GuidedTour.md +++ b/TSPL.docc/GuidedTour/GuidedTour.md @@ -381,6 +381,21 @@ the conditional must be a Boolean expression --- this means that code such as `if score { ... }` is an error, not an implicit comparison to zero. +You can write `if` or `switch` +after the equals sign (`=`) of an assignment +or after `return`, +to choose a value based on the condition. + +```swift +let scoreDecoration = if teamScore > 10 { + "🎉" +} else { + "" +} +print("Score:", teamScore, scoreDecoration) +// Prints "Score: 11 🎉" +``` + You can use `if` and `let` together to work with values that might be missing. These values are represented as optionals. From e9edbd7d2abcb0acca6ab68ae8ad58f8457d5414 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 6 Mar 2023 16:59:11 -0800 Subject: [PATCH 07/21] Iterate on reference and grammar. Looking at the test cases from compiler PR 63022 and testing out various behavior myself using the 2023-03-02-a development snapshot. --- TSPL.docc/LanguageGuide/ControlFlow.md | 2 +- TSPL.docc/ReferenceManual/Expressions.md | 44 ++++++++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index a5595664c..c09417e13 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -788,7 +788,7 @@ which is printed after the `if` statement. Using the alternate syntax, known as an `if` expression, -you could write this code more concisely: +you can write this code more concisely: ```swift let weatherAdvice = if temperatureInCelsius <= 0 { diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 74dfd0350..5574cabe8 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -950,23 +950,28 @@ A conditional expression has the same behavior and syntax as an if statement or switch statement, with the following differences: -- A conditional expression can appear only in the following contexts: +- A conditional expression appears only in the following contexts: - As the value assigned to a variable. - As the initial value in a variable or constant declaration. + - As the error thrown by a `throw` expression. + - As the value returned by a function, closure, or property getter. -- Each branch must contain a single expression, +- Each branch contains either a single expression, which is used as the value for the conditional expression - when that branch's conditional is true. - - + when that branch's conditional is true, + a `throw` statement, + or a call to a function that never returns. + -- An `if` branch must have a corresponding `else` branch. +- Each `if` branch has a corresponding `else` branch. -- Each branch must produce a value of the same type. +- Each branch produces a value of the same type. Type checking of each branch happens independently. +Don't put a conditional expression in a `try` expression, +even if one of the branches of a conditional expression is throwing. + + + > Grammar of a conditional expression: > -> *if-expression* → **`if`** *condition-list* **`{`** expression **`}`** *if-expression-tail* -> *if-expression-tail* → **`else`** *condition-list* **`{`** expression **`}`** *if-expression-tail* +> *if-expression* → **`if`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> > *if-expression-tail* → **`else`** *if-expression* > +> *if-expression-tail* → **`else`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> > > > *switch-expression* → **`switch`** *expression* **`{`** *switch-expression-cases* **`}`** > > *switch-expression-cases* → *switch-expression-case* *switch-expression-cases*_?_ > -> *switch-expression-case* → *case-label* *expression* +> *switch-expression-case* → *case-label* *statement* > -> *switch-expression-case* → *default-label* *expression* +> *switch-expression-case* → *default-label* *statement* ### Closure Expression From 32e05c5e986c904bae3d7a7213a739527d0383a2 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 7 Mar 2023 16:30:52 -0800 Subject: [PATCH 08/21] Show failure cases in if/switch expressions. --- TSPL.docc/LanguageGuide/ControlFlow.md | 42 ++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index c09417e13..17016354c 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -819,13 +819,34 @@ each branch of the `if` expression produces of the three possible values for `weatherAdvice`, and the assignment uses that value. +An `if` expression that checks for conditions +that are invalid or that it can't handle +can throw an error +or call a function like `fatalError(_:file:line:)` that never returns. +For example: + +```swift +let weatherAdvice = if temperatureInCelsius > 100 { + throw TemperatureError.boiling +} else { + "It's a reasonable temperature." +} +``` + +In this example, +the `if` expression checks whether the forecast temperature +is hotter than 100° C, the temperature that water boils at. +A temperature this hot causes the `if` expression to throw a `.boiling` error +instead of returning a textual summary. +Even though this `if` expression can throw an error, +you don't write `try` before it. +For information about working with errors, see . + You can use `if` expressions on the right-hand side of an assignment, as shown in the example above, and as the value that a function or closure returns. - - ### Switch A `switch` statement considers a value @@ -929,7 +950,22 @@ print(message) // Prints "The first letter of the alphabet" ``` - +In this example, +each case in the `switch` expression +contains the value for `message` +to be used when that case matches `anotherCharacter`. +Because `switch` is always exhaustive, +there will always be a value to assign. + +As with `if` expressions, +you can throw an error +or call a function like `fatalError(_:file:line:)` that never returns +instead of providing a value for a given case. +You can use `switch` expressions +on the right-hand side of an assignment, +as shown in the example above, +and as the value that a function or closure returns. + #### No Implicit Fallthrough From eed7461fe1340b5cf1d8c87a1eed831883f97f03 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 7 Mar 2023 16:31:23 -0800 Subject: [PATCH 09/21] Avoid making the whole section one big list. --- TSPL.docc/ReferenceManual/Expressions.md | 72 ++++++++++-------------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 5574cabe8..f76ace9e5 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -947,61 +947,49 @@ default: ``` A conditional expression -has the same behavior and syntax as an if statement or switch statement, -with the following differences: +has the same behavior and syntax as an `if` statement or a `switch` statement, +except for the differences listed below. -- A conditional expression appears only in the following contexts: +A conditional expression appears only in the following contexts: - - As the value assigned to a variable. + - As the value assigned to a variable. + - As the initial value in a variable or constant declaration. + - As the error thrown by a `throw` expression. + - As the value returned by a function, closure, or property getter. - - As the initial value in a variable or constant declaration. +The branches of a conditional expression are exhaustive, +ensuring that the expression always produces a value +regardless of the condition. +This means each `if` branch needs a corresponding `else` branch. - - As the error thrown by a `throw` expression. +Each branch contains either a single expression, +which is used as the value for the conditional expression +when that branch's conditional is true, +a `throw` statement, +or a call to a function that never returns. - - As the value returned by a function, closure, or property getter. +Each branch must produce a value of the same type. +Because type checking of each branch is independent, +conditional expressions that that include a `nil` literal +or different kinds of literals typically need type context, +like an explicit type annotation. -- Each branch contains either a single expression, - which is used as the value for the conditional expression - when that branch's conditional is true, - a `throw` statement, - or a call to a function that never returns. - - -- Each `if` branch has a corresponding `else` branch. - -- Each branch produces a value of the same type. - Type checking of each branch happens independently. +```swift +let number: Double = if someCondition { 10 } else {12.34 } +``` - +A conditional expression can't appear inside a result builder. -- A conditional expression can't appear inside a result builder. + +The variable declaration form of an if will be allowed in result builders. +--> Don't put a conditional expression in a `try` expression, even if one of the branches of a conditional expression is throwing. - - > Grammar of a conditional expression: > > *if-expression* → **`if`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* From c1d595eb0c58e7423f3987e2e12ad9f7598eb560 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 7 Mar 2023 17:26:43 -0800 Subject: [PATCH 10/21] Update the summary of the grammar. --- .../ReferenceManual/SummaryOfTheGrammar.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md index 1c9d19aa2..86fcd2d96 100644 --- a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md +++ b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md @@ -639,6 +639,24 @@ make the same change here also. > > *superclass-initializer-expression* → **`super`** **`.`** **`init`** +> Grammar of a conditional expression: +> +> *if-expression* → **`if`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> +> *if-expression-tail* → **`else`** *if-expression* +> +> *if-expression-tail* → **`else`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> +> +> +> *switch-expression* → **`switch`** *expression* **`{`** *switch-expression-cases* **`}`** +> +> *switch-expression-cases* → *switch-expression-case* *switch-expression-cases*_?_ +> +> *switch-expression-case* → *case-label* *statement* +> +> *switch-expression-case* → *default-label* *statement* + > Grammar of a closure expression: > > *closure-expression* → **`{`** *attributes*_?_ *closure-signature*_?_ *statements*_?_ **`}`** From faec17e0c183c8ef0e71b0011d494a4ffdb99b05 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 9 Mar 2023 11:37:46 -0800 Subject: [PATCH 11/21] Incorporate tech review from Hamish. - Note that conditional expressions can nest. - Clarify conditional expressions behavior inside a result builder. - Fix copy/paste error in the grammar. Co-authored-by: Hamish Knight --- TSPL.docc/ReferenceManual/Expressions.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index f76ace9e5..f1b1153f3 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -956,6 +956,7 @@ A conditional expression appears only in the following contexts: - As the initial value in a variable or constant declaration. - As the error thrown by a `throw` expression. - As the value returned by a function, closure, or property getter. + - As the value inside a branch of a conditional expression. The branches of a conditional expression are exhaustive, ensuring that the expression always produces a value @@ -978,14 +979,13 @@ like an explicit type annotation. let number: Double = if someCondition { 10 } else {12.34 } ``` -A conditional expression can't appear inside a result builder. - - +Inside a result builder, +conditional expressions can appear +only as the initial value of a variable or constant. +This behavior means when you write `if` or `switch` in a result builder, +outside of a variable or constant declaration, +that code is understood as a branch statement +and one of the result builder's methods transforms that code. Don't put a conditional expression in a `try` expression, even if one of the branches of a conditional expression is throwing. @@ -996,7 +996,7 @@ even if one of the branches of a conditional expression is throwing. > > *if-expression-tail* → **`else`** *if-expression* > -> *if-expression-tail* → **`else`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> *if-expression-tail* → **`else`** **`{`** *statement* **`}`** *if-expression-tail* > > > From 1d6516179647ca194ec54fb1715d1376b5a6ba79 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 9 Mar 2023 13:06:28 -0800 Subject: [PATCH 12/21] Remove stray backticks. --- TSPL.docc/LanguageGuide/ControlFlow.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 17016354c..e15d6f8a6 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -764,8 +764,6 @@ that you can use to set a value based on certain conditions. For example, consider the following code that uses `if` statements: -``` - ```swift let temperatureInCelsius = 25 let weatherAdvice: String From 4c5bc888d7bdf97c95eb9d7e60329fd7b16bc0cf Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 9 Mar 2023 14:16:14 -0800 Subject: [PATCH 13/21] Add an example that needs a type annotation. Per suggestion from Hamish Knight . --- TSPL.docc/LanguageGuide/ControlFlow.md | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index e15d6f8a6..cd00eed83 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -817,6 +817,39 @@ each branch of the `if` expression produces of the three possible values for `weatherAdvice`, and the assignment uses that value. +All of the branches of an `if` expression +need to contain values of the same type. +Because Swift checks the type of each branch separately, +values like `nil` that can be used with multiple types +prevent Swift from determining the `if` expression's type automatically. +Instead, you need to specify the type explicitly --- +for example: + +```swift +let freezeWarning: String? = if temperatureInCelsius <= 0 { + "It's below freezing. Watch for ice!" +} else { + nil +} +``` + +In the code above, +one branch of the `if` statement has a string value +and the other branch has a `nil` value. +The `nil` value could be used as a value for any optional type, +so you have to explicitly write that `freezeWarning` is an optional string. +An alternate way to provide this type information +is to provide an explicit type for `nil`, +instead of providing an explicit type for `freezeWarning`: + +```swift +let freezeWarning = if temperatureInCelsius <= 0 { + "It's below freezing. Watch for ice!" +} else { + nil as String? +} +``` + An `if` expression that checks for conditions that are invalid or that it can't handle can throw an error From 4cb2d9eb615d00cc56b58512adeba3650bdc7480 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 9 Mar 2023 14:22:57 -0800 Subject: [PATCH 14/21] Remove stray double-newlines. --- TSPL.docc/GuidedTour/GuidedTour.md | 3 --- TSPL.docc/LanguageGuide/ControlFlow.md | 1 - 2 files changed, 4 deletions(-) diff --git a/TSPL.docc/GuidedTour/GuidedTour.md b/TSPL.docc/GuidedTour/GuidedTour.md index a7aa0fcc2..59782f6a6 100644 --- a/TSPL.docc/GuidedTour/GuidedTour.md +++ b/TSPL.docc/GuidedTour/GuidedTour.md @@ -221,8 +221,6 @@ A comma is allowed after the last element. Occupations is a reference to Firefly, specifically to Mal's joke about Jayne's job on the ship. - - Can't find the specific episode, but it shows up in several lists of Firefly "best of" quotes: @@ -310,7 +308,6 @@ let emptyArray: [String] = [] let emptyDictionary: [String: Float] = [:] ``` - -Note that *identical to* (represented by three equals signs, or `===`) -doesn't mean the same thing as *equal to* (represented by two equals signs, or `==`). +Note that *identical to* (represented by three equal signs, or `===`) +doesn't mean the same thing as *equal to* (represented by two equal signs, or `==`). *Identical to* means that two constants or variables of class type refer to exactly the same class instance. *Equal to* means that diff --git a/TSPL.docc/ReferenceManual/Declarations.md b/TSPL.docc/ReferenceManual/Declarations.md index 0ad3a355a..1fdf53089 100644 --- a/TSPL.docc/ReferenceManual/Declarations.md +++ b/TSPL.docc/ReferenceManual/Declarations.md @@ -1055,7 +1055,7 @@ For example, the variadic parameter `Int...` is treated as `[Int]`. For an example that uses a variadic parameter, see . -A parameter with an equals sign (`=`) and an expression after its type +A parameter with an equal sign (`=`) and an expression after its type is understood to have a default value of the given expression. The given expression is evaluated when the function is called. If the parameter is omitted when calling the function, From 31abb13a8d9aec70e05d1f8375503a601614dd2a Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 26 Apr 2023 13:44:53 -0700 Subject: [PATCH 18/21] Style fix: t-shirt -> T-shirt Co-authored-by: Chuck Toporek --- TSPL.docc/LanguageGuide/ControlFlow.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 637d37321..31ae4a985 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -672,9 +672,9 @@ temperatureInFahrenheit = 40 if temperatureInFahrenheit <= 32 { print("It's very cold. Consider wearing a scarf.") } else { - print("It's not that cold. Wear a t-shirt.") + print("It's not that cold. Wear a T-shirt.") } -// Prints "It's not that cold. Wear a t-shirt." +// Prints "It's not that cold. Wear a T-shirt." ``` @@ -706,7 +706,7 @@ if temperatureInFahrenheit <= 32 { } else if temperatureInFahrenheit >= 86 { print("It's really warm. Don't forget to wear sunscreen.") } else { - print("It's not that cold. Wear a t-shirt.") + print("It's not that cold. Wear a T-shirt.") } // Prints "It's really warm. Don't forget to wear sunscreen." ``` @@ -721,7 +721,7 @@ if temperatureInFahrenheit <= 32 { } else if temperatureInFahrenheit >= 86 { print("It's really warm. Don't forget to wear sunscreen.") } else { - print("It's not that cold. Wear a t-shirt.") + print("It's not that cold. Wear a T-shirt.") } <- It's really warm. Don't forget to wear sunscreen. ``` @@ -773,11 +773,11 @@ if temperatureInCelsius <= 0 { } else if temperatureInCelsius >= 30 { weatherAdvice = "It's really warm. Don't forget to wear sunscreen." } else { - weatherAdvice = "It's not that cold. Wear a t-shirt." + weatherAdvice = "It's not that cold. Wear a T-shirt." } print(weatherAdvice) -// Prints "It's not that cold. Wear a t-shirt." +// Prints "It's not that cold. Wear a T-shirt." ``` Here, each of the branches sets a value for the `weatherAdvice` constant, @@ -794,11 +794,11 @@ let weatherAdvice = if temperatureInCelsius <= 0 { } else if temperatureInCelsius >= 30 { "It's really warm. Don't forget to wear sunscreen." } else { - "It's not that cold. Wear a t-shirt." + "It's not that cold. Wear a T-shirt." } print(weatherAdvice) -// Prints "It's not that cold. Wear a t-shirt." +// Prints "It's not that cold. Wear a T-shirt." ``` In this `if` expression version of the code, From 0d492dc52dac46fac053b18faca81efabd76f4f6 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 26 Apr 2023 13:46:36 -0700 Subject: [PATCH 19/21] Mention which alphabet. Co-authored-by: Chuck Toporek --- TSPL.docc/LanguageGuide/ControlFlow.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 31ae4a985..43d59fc28 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -929,13 +929,13 @@ a single lowercase character called `someCharacter`: let someCharacter: Character = "z" switch someCharacter { case "a": - print("The first letter of the alphabet") + print("The first letter of the Latin alphabet") case "z": - print("The last letter of the alphabet") + print("The last letter of the Latin alphabet") default: print("Some other character") } -// Prints "The last letter of the alphabet" +// Prints "The last letter of the Latin alphabet" ``` @@ -971,15 +971,15 @@ Like `if` statements, let anotherCharacter: Character = "a" let message = switch anotherCharacter { case "a": - "The first letter of the alphabet" + "The first letter of the Latin alphabet" case "z": - "The last letter of the alphabet" + "The last letter of the Latin alphabet" default: "Some other character" } print(message) -// Prints "The first letter of the alphabet" +// Prints "The first letter of the Latin alphabet" ``` In this example, From 69930646a98da77c6d75a5c7db8a7b0e59bbaa72 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 26 Apr 2023 14:14:30 -0700 Subject: [PATCH 20/21] Apply editorial fixes. Co-authored-by: Chuck Toporek --- TSPL.docc/LanguageGuide/ControlFlow.md | 10 +++++----- TSPL.docc/ReferenceManual/Expressions.md | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/TSPL.docc/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 43d59fc28..299ba5a65 100644 --- a/TSPL.docc/LanguageGuide/ControlFlow.md +++ b/TSPL.docc/LanguageGuide/ControlFlow.md @@ -801,7 +801,7 @@ print(weatherAdvice) // Prints "It's not that cold. Wear a T-shirt." ``` -In this `if` expression version of the code, +In this `if` expression version, each branch contains a single value. If a branch's condition is true, then that branch's value is used as the value for the whole `if` expression @@ -812,10 +812,10 @@ and that the `if` expression always produces a value, regardless of which conditions are true. Because the syntax for the assignment starts outside the `if` expression, -there's no need to repeat `weatherAdvice =` inside each branch --- -instead, +there's no need to repeat `weatherAdvice =` inside each branch. +Instead, each branch of the `if` expression -produces of the three possible values for `weatherAdvice`, +produces one of the three possible values for `weatherAdvice`, and the assignment uses that value. All of the branches of an `if` expression @@ -867,7 +867,7 @@ let weatherAdvice = if temperatureInCelsius > 100 { In this example, the `if` expression checks whether the forecast temperature -is hotter than 100° C, the temperature that water boils at. +is hotter than 100° C --- the boiling point of water. A temperature this hot causes the `if` expression to throw a `.boiling` error instead of returning a textual summary. Even though this `if` expression can throw an error, diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 0ccb15a53..2b1fa0912 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -973,10 +973,10 @@ or a call to a function that never returns. Each branch must produce a value of the same type. Because type checking of each branch is independent, -you sometimes need to add type context, +you sometimes need to specify the value's type explicitly, like when branches include different kinds of literals, or when a branch's value is `nil`. -To provide this type context, +When you need to provide this information, add a type annotation to the variable that the result is assigned to, or add an `as` cast to the branches' values. @@ -988,8 +988,8 @@ let number = if someCondition { 10 as Double } else { 12.34 } Inside a result builder, conditional expressions can appear only as the initial value of a variable or constant. -This behavior means when you write `if` or `switch` in a result builder, -outside of a variable or constant declaration, +This behavior means when you write `if` or `switch` in a result builder --- +outside of a variable or constant declaration --- that code is understood as a branch statement and one of the result builder's methods transforms that code. From 9b37cf13789d1fdb6625972a34928a6d8bc68630 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 28 Apr 2023 11:05:17 -0700 Subject: [PATCH 21/21] Clarify "below". Co-authored-by: Chuck Toporek --- TSPL.docc/ReferenceManual/Expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 2b1fa0912..54d6893aa 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -950,7 +950,7 @@ default: A conditional expression has the same behavior and syntax as an `if` statement or a `switch` statement, -except for the differences listed below. +except for the differences that the paragraphs below describe. A conditional expression appears only in the following contexts: