diff --git a/TSPL.docc/GuidedTour/GuidedTour.md b/TSPL.docc/GuidedTour/GuidedTour.md index e310fad2c..b1432ea8b 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/LanguageGuide/ControlFlow.md b/TSPL.docc/LanguageGuide/ControlFlow.md index 4cfd7ce17..299ba5a65 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. ``` @@ -759,6 +759,126 @@ 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 a shorthand spelling of `if` +that you can use when setting values. +For example, +consider the following code: + +```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 can 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, +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 +in the assignment of `weatherAdvice`. +Every `if` branch has a corresponding `else if` branch or `else` branch, +ensuring that one of the branches always matches +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, +each branch of the `if` expression +produces one 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 more than one type +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` expression 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, +as described in . + +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 can respond to unexpected failures by throwing an error +or calling 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 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, +you don't write `try` before it. +For information about working with errors, see . + +In addition to using `if` expressions +on the right-hand side of an assignment, +as shown in the examples above, +you can also use them as the value that a function or closure returns. + ### Switch A `switch` statement considers a value @@ -809,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" ``` @@ -844,6 +964,40 @@ 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 Latin alphabet" +case "z": + "The last letter of the Latin alphabet" +default: + "Some other character" +} + +print(message) +// Prints "The first letter of the Latin 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 is always 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 In contrast with `switch` statements in C and Objective-C, 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, diff --git a/TSPL.docc/ReferenceManual/Expressions.md b/TSPL.docc/ReferenceManual/Expressions.md index 6fa8ba59a..54d6893aa 100644 --- a/TSPL.docc/ReferenceManual/Expressions.md +++ b/TSPL.docc/ReferenceManual/Expressions.md @@ -554,6 +554,8 @@ to make prefix expressions, infix expressions, and postfix expressions. > > *primary-expression* → *superclass-expression* > +> *primary-expression* → *conditional-expression* +> > *primary-expression* → *closure-expression* > > *primary-expression* → *parenthesized-expression* @@ -921,6 +923,101 @@ to make use of the implementation in their superclass. > > *superclass-initializer-expression* → **`super`** **`.`** **`init`** +### Conditional Expression + +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#> +} + +switch <#expression#> { +case <#pattern 1#>: + <#expression 1#> +case <#pattern 2#> where <#condition#>: + <#expression 2#> +default: + <#expression 3#> +} +``` + +A conditional expression +has the same behavior and syntax as an `if` statement or a `switch` statement, +except for the differences that the paragraphs below describe. + +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. + - 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 +regardless of the condition. +This means each `if` branch needs a corresponding `else` branch. + +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 branch must produce a value of the same type. +Because type checking of each branch is independent, +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`. +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. + +```swift +let number: Double = if someCondition { 10 } else { 12.34 } +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 --- +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. + +> Grammar of a conditional expression: +> +> *conditional-expression* → *if-expression* | *switch-expression* +> +> +> +> *if-expression* → **`if`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> +> *if-expression-tail* → **`else`** *if-expression* +> +> *if-expression-tail* → **`else`** **`{`** *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* + ### Closure Expression A *closure expression* creates a closure, diff --git a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md index 1c9d19aa2..ac65a0037 100644 --- a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md +++ b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md @@ -565,6 +565,8 @@ make the same change here also. > > *primary-expression* → *superclass-expression* > +> *primary-expression* → *conditional-expression* +> > *primary-expression* → *closure-expression* > > *primary-expression* → *parenthesized-expression* @@ -639,6 +641,28 @@ make the same change here also. > > *superclass-initializer-expression* → **`super`** **`.`** **`init`** +> Grammar of a conditional expression: +> +> *conditional-expression* → *if-expression* | *switch-expression* +> +> +> +> *if-expression* → **`if`** *condition-list* **`{`** *statement* **`}`** *if-expression-tail* +> +> *if-expression-tail* → **`else`** *if-expression* +> +> *if-expression-tail* → **`else`** **`{`** *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*_?_ **`}`**