-
Notifications
You must be signed in to change notification settings - Fork 157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expand and reframe discussion of optionals #143
Conversation
Made the merge by copying the contents of a Git working copy at 'main'
over to this working copy, replacing what was on this branch. Confirmed
both 'main' and this branch have the same content by comparing the Git
tree that the commits point to:
% git cat-file -p HEAD | grep tree
tree c86b6dd88a6ff5df836cdf53124e213c8f0f0009
% git cat-file -p main | grep tree
tree c86b6dd88a6ff5df836cdf53124e213c8f0f0009
Generated a patch of this branch's changes using git-diff. Manually
re-applied those changes to the markdown file. Confirmed that the
branch still makes the same changes by comparing the diffs:
opendiff \
=(git diff --patience --word-diff=porcelain main...optional_41014749_RST) \
=(git diff --patience --word-diff=porcelain optional_41014749^-)
Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com>
Code like the following is an anti-pattern -- Swift has better idioms
for working with optionals:
if x != nil { doSomething(x!) }
The only reason to compare == or != nil is for flow control -- so moved
the information there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see edits and comments within.
| To write an optional type, | ||
| you write a question mark (`?`) | ||
| after the name of the type that the optional contains --- | ||
| for example, the type of an optional `Int` is `Int?`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| for example, the type of an optional `Int` is `Int?`. | |
| for example, `Int?`. |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| for example, the type of an optional `Int` is `Int?`. | ||
| An optional `Int` always contains | ||
| either some `Int` value or no value at all. | ||
| It can't contain anything else, like a `Bool` value or a `String` value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| It can't contain anything else, like a `Bool` value or a `String` value. | |
| It can't contain anything else, like a `Bool` or `String` value. |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| > If a constant or variable in your code needs to work with | ||
| > the absence of a value under certain conditions, | ||
| > always declare it as an optional value of the appropriate type. | ||
|
|
||
| If you define an optional variable without providing a default value, | ||
| the variable is automatically set to `nil` for you: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| the variable is automatically set to `nil` for you: | |
| the variable is automatically set to `nil`: |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| If a constant or variable in your code needs to work with | ||
| the absence of a value under certain conditions, | ||
| declare it as an optional value of the appropriate type. | ||
| A constant or variable that's declared as a non-optional value, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| A constant or variable that's declared as a non-optional value, | |
| A constant or variable that's declared as a non-optional value |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| ``` | ||
| This separation of optional and non-optional values | ||
| lets you explicitly mark what information can be missing, | ||
| and makes it easier to write correct code that handle missing values. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| and makes it easier to write correct code that handle missing values. | |
| and makes it easier to write code that handles missing values. |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
|
|
||
| For more information about enforcing data requirements | ||
| and checking assumptions at runtime, | ||
| see <doc:TheBasics#Assertions-and-Preconditions> below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| see <doc:TheBasics#Assertions-and-Preconditions> below. | |
| see <doc:TheBasics#Assertions-and-Preconditions>. |
| @@ -1570,6 +1623,11 @@ and can definitely be assumed to exist at every point thereafter. | |||
| The primary use of implicitly unwrapped optionals in Swift is during class initialization, | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| The primary use of implicitly unwrapped optionals in Swift is during class initialization, | |
| Use implicitly unwrapped optionals in Swift when initializing a class, |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| Trying to recover from an invalid state isn't possible --- | ||
| because the assertion failed, | ||
| you know that at least one piece of the program's data is invalid, | ||
| but you don't know why it's invalid | ||
| or what additional state is also invalid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Trying to recover from an invalid state isn't possible --- | |
| because the assertion failed, | |
| you know that at least one piece of the program's data is invalid, | |
| but you don't know why it's invalid | |
| or what additional state is also invalid. | |
| Recovering from an invalid state is impossible. | |
| When an assertion fails, | |
| at least one piece of the program's data is invalid --- | |
| but you don't know why it's invalid | |
| or whether an additional state is also invalid. |
| > Optionals of *any* type can be set to `nil`, not just object types. | ||
|
|
||
| ### If Statements and Forced Unwrapping | ||
|
|
||
| You can use an `if` statement to find out whether an optional contains a value | ||
| by comparing the optional against `nil`. | ||
| You perform this comparison with the “equal to” operator (`==`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| You perform this comparison with the “equal to” operator (`==`) | |
| You perform this comparison with the equal-to operator (`==`) |
TSPL.docc/LanguageGuide/TheBasics.md
Outdated
| @@ -1308,6 +1280,9 @@ or the “not equal to” operator (`!=`). | |||
| If an optional has a value, it's considered to be “not equal to” `nil`: | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| If an optional has a value, it's considered to be “not equal to” `nil`: | |
| If an optional has a value, it's considered as not-equal-to, `nil`: |
Fixes: rdar://111129109 Co-authored-by: Chuck Toporek <chuck_toporek@apple.com>
|
On Aug 18, 2023, at 4:53 PM, Chuck Toporek ***@***.***> wrote:
@chuckdude requested changes on this pull request.
Please see edits and comments within.In TSPL.docc/LanguageGuide/TheBasics.md:
> it returns an *optional* `Int`, rather than an `Int`.
-An optional `Int` is written as `Int?`, not `Int`.
-The question mark indicates that the value it contains is optional,
-meaning that it might contain *some* `Int` value,
-or it might contain *no value at all*.
-(It can't contain anything else, such as a `Bool` value or a `String` value.
-It's either an `Int`, or it's nothing at all.)
+
+To write an optional type,
+you write a question mark (`?`)
+after the name of the type that the optional contains ---
+for example, the type of an optional `Int` is `Int?`.
⬇️ Suggested change -for example, the type of an optional `Int` is `Int?`.
+for example, `Int?`.
Stet; this is the first time the reader is seeing an optional type like Int? so we need to define it.
In TSPL.docc/LanguageGuide/TheBasics.md:
> it returns an *optional* `Int`, rather than an `Int`.
-An optional `Int` is written as `Int?`, not `Int`.
-The question mark indicates that the value it contains is optional,
-meaning that it might contain *some* `Int` value,
-or it might contain *no value at all*.
-(It can't contain anything else, such as a `Bool` value or a `String` value.
-It's either an `Int`, or it's nothing at all.)
+
+To write an optional type,
+you write a question mark (`?`)
+after the name of the type that the optional contains ---
+for example, the type of an optional `Int` is `Int?`.
+An optional `Int` always contains
+either some `Int` value or no value at all.
+It can't contain anything else, like a `Bool` value or a `String` value.
⬇️ Suggested change -It can't contain anything else, like a `Bool` value or a `String` value.
+It can't contain anything else, like a `Bool` or `String` value.
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1271,11 +1255,6 @@ serverResponseCode = nil
```
-->
-> Note: You can't use `nil` with non-optional constants and variables.
-> If a constant or variable in your code needs to work with
-> the absence of a value under certain conditions,
-> always declare it as an optional value of the appropriate type.
-
If you define an optional variable without providing a default value,
the variable is automatically set to `nil` for you:
⬇️ Suggested change -the variable is automatically set to `nil` for you:
+the variable is automatically set to `nil`:
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1325,47 +1300,55 @@ if convertedNumber != nil {
```
-->
-Once you're sure that the optional *does* contain a value,
-you can access its underlying value
-by adding an exclamation point (`!`) to the end of the optional's name.
-The exclamation point effectively says,
-“I know that this optional definitely has a value; please use it.”
-This is known as *forced unwrapping* of the optional's value:
+You can't use `nil` with non-optional constants or variables.
+If a constant or variable in your code needs to work with
+the absence of a value under certain conditions,
+declare it as an optional value of the appropriate type.
+A constant or variable that's declared as a non-optional value,
⬇️ Suggested change -A constant or variable that's declared as a non-optional value,
+A constant or variable that's declared as a non-optional value
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
>
-```swift
-if convertedNumber != nil {
- print("convertedNumber has an integer value of \(convertedNumber!).")
-}
-// Prints "convertedNumber has an integer value of 123."
-```
+This separation of optional and non-optional values
+lets you explicitly mark what information can be missing,
+and makes it easier to write correct code that handle missing values.
⬇️ Suggested change -and makes it easier to write correct code that handle missing values.
+and makes it easier to write code that handles missing values.
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
>
-<!--
- - test: `optionals`
+When you access an optional value,
+your code always handles both the `nil` and non-`nil` case.
+There are several things you can do when a value is missing,
+which are described in more detail in the following sections:
⬇️ Suggested change -which are described in more detail in the following sections:
+as described in the following sections:
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
>
### Optional Binding
-You use *optional binding* to find out whether an optional contains a value,
+You use optional binding to find out whether an optional contains a value,
Should this be "an optional binding"?
Existing content omits the article before "optional binding" throughout. I think the best explanation is that optional binding is a technique — for example, we'd write "use pattern matching to check the value".
I added an entry to the TSPL style guide to call this out.
In TSPL.docc/LanguageGuide/TheBasics.md:
> and if so, to make that value available as a temporary constant or variable.
-Optional binding can be used with `if` and `while` statements
+Optional binding can be used with `if`, `guard`, and `while` statements
⬇️ Suggested change -Optional binding can be used with `if`, `guard`, and `while` statements
+Use optional binding with `if`, `guard`, and `while` statements
Suggest stet — this is not an instruction, it's a statement of where optional binding is available. An alternate rewrite:
Use optional binding with `if`, `guard`, and `while` statements
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1410,8 +1393,9 @@ If the conversion is successful,
the `actualNumber` constant becomes available for use within
the first branch of the `if` statement.
It has already been initialized with the value contained *within* the optional,
⬇️ Suggested change -It has already been initialized with the value contained *within* the optional,
+It has already been initialized with the value contained within the optional,
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1410,8 +1393,9 @@ If the conversion is successful,
the `actualNumber` constant becomes available for use within
the first branch of the `if` statement.
It has already been initialized with the value contained *within* the optional,
-and so you don't use the `!` suffix to access its value.
-In this example, `actualNumber` is simply used to print the result of the conversion.
+and has the corresponding non-optional type.
+In this case, the type of `possibleNumber` is `Int?`
⬇️ Suggested change -In this case, the type of `possibleNumber` is `Int?`
+In this case, the type of `possibleNumber` is `Int?`,
Changed.
I think we were trying to avoid a "punctuation pile-up" especially for readers who are still getting used to ? as a postfix.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1448,11 +1432,11 @@ the value of a new constant named `myNumber` is set to that value.
Inside the body of the `if` statement,
writing `myNumber` refers to that new non-optional constant.
Before the beginning of the `if` statement and after its end,
-writing `myNumber` refers to the optional integer constant.
+writing `myNumber` refers to the original optional integer constant.
Suggest rewriting this sentence as:
Writing myNumber before or after the if statement refers to the original optional integer constant.
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> -> Note: Constants and variables created with optional binding in an `if` statement
-> are available only within the body of the `if` statement.
-> In contrast, the constants and variables created with a `guard` statement
-> are available in the lines of code that follow the `guard` statement,
-> as described in <doc:ControlFlow#Early-Exit>.
+Constants and variables created with optional binding in an `if` statement
+are available only within the body of the `if` statement.
+In contrast, the constants and variables created with a `guard` statement
+are available in the lines of code that follow the `guard` statement,
+as described in <doc:ControlFlow#Early-Exit>.
+
+### Providing a Fallback Value
+
+Another way to handle a missing value is to supply
+a default value using the nil-coalescing operator (`??`).
+If the optional on the left side of the `??` isn't `nil`,
⬇️ Suggested change -If the optional on the left side of the `??` isn't `nil`,
+If the optional on the left of the `??` isn't `nil`,
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> -> In contrast, the constants and variables created with a `guard` statement
-> are available in the lines of code that follow the `guard` statement,
-> as described in <doc:ControlFlow#Early-Exit>.
+Constants and variables created with optional binding in an `if` statement
+are available only within the body of the `if` statement.
+In contrast, the constants and variables created with a `guard` statement
+are available in the lines of code that follow the `guard` statement,
+as described in <doc:ControlFlow#Early-Exit>.
+
+### Providing a Fallback Value
+
+Another way to handle a missing value is to supply
+a default value using the nil-coalescing operator (`??`).
+If the optional on the left side of the `??` isn't `nil`,
+that value is unwrapped and used.
+Otherwise, the value on the right side of `??` is used.
⬇️ Suggested change -Otherwise, the value on the right side of `??` is used.
+Otherwise, the value on the right of `??` is used.
Changed.
In TSPL.docc/LanguageGuide/TheBasics.md:
> + -> let name: String? = nil
+ -> let greeting = "Hello, " + (name ?? "friend") + "!"
+ -> print(greeting)
+ <- Hello, friend!
+ ```
+-->
+
+For more information about using `??` to provide a fallback value,
+see <doc:BasicOperators#Nil-Coalescing-Operator>.
+
+### Force Unwrapping
+
+When `nil` represents an unrecoverable failure,
+such a programmer error or corrupted state,
+you can access the underlying value
+by adding an exclamation mark (`!`) to the end of the optional's name.
⬇️ Suggested change -by adding an exclamation mark (`!`) to the end of the optional's name.
+by adding an exclamation point (`!`) to the end of the optional's name.
Changed.
Good catch — searched the rest of TSPL for "exclamation mark" and this was the only one that came back after commit 0306251 in 2020 fixed these up for Apple style. This text came from a branch that diverged from 'main' before that fix.
In TSPL.docc/LanguageGuide/TheBasics.md:
> +
+For more information about using `??` to provide a fallback value,
+see <doc:BasicOperators#Nil-Coalescing-Operator>.
+
+### Force Unwrapping
+
+When `nil` represents an unrecoverable failure,
+such a programmer error or corrupted state,
+you can access the underlying value
+by adding an exclamation mark (`!`) to the end of the optional's name.
+This is known as *force unwrapping* the optional's value.
+When you force unwrap a non-`nil` value,
+the result is its unwrapped value.
+Force unwrapping a `nil` value triggers a runtime error.
+
+The `!` is, effectively, a shorter spelling of [`fatalError(_:file:line:)`][].
⬇️ Suggested change -The `!` is, effectively, a shorter spelling of [`fatalError(_:file:line:)`][].
+The `!` is a shorter spelling of [`fatalError(_:file:line:)`][].
Let's find a different rewording. The `!` isn't literally or exactly a shorter spelling of `fatalError(...)` — it does the same thing, but the crash traceback will look different.
In TSPL.docc/LanguageGuide/TheBasics.md:
> +```swift
+let possibleNumber = "123"
+let convertedNumber = Int(possibleNumber)
+
+let number = convertedNumber!
+
+guard let number = convertedNumber else {
+ fatalError("The number was invalid")
+}
+```
+
+Both versions of the code above depend on `convertedNumber`
+always containing a value.
+Writing that requirement as part of the code,
+using either of the approaches above,
+lets your code check that the requirement is true at run time.
⬇️ Suggested change -lets your code check that the requirement is true at run time.
+lets your code check that the requirement is true at runtime.
Changed to follow ASG.
Someday we may need to consider an exception to this rule (as noted in the TSPL style guide) if we start documenting the Swift runtime and need to distinguish between things that are part of the Swift runtime environment, and things that happen when the code is running (at run time).
In TSPL.docc/LanguageGuide/TheBasics.md:
> +let number = convertedNumber!
+
+guard let number = convertedNumber else {
+ fatalError("The number was invalid")
+}
+```
+
+Both versions of the code above depend on `convertedNumber`
+always containing a value.
+Writing that requirement as part of the code,
+using either of the approaches above,
+lets your code check that the requirement is true at run time.
+
+For more information about enforcing data requirements
+and checking assumptions at runtime,
+see <doc:TheBasics#Assertions-and-Preconditions> below.
⬇️ Suggested change -see <doc:TheBasics#Assertions-and-Preconditions> below.
+see <doc:TheBasics#Assertions-and-Preconditions>.
Changed; the "below" is fairly typical of TSPL forward references within a chapter:
```
LanguageGuide/Concurrency.md: as shown in <doc:Concurrency#Unstructured-Concurrency> below.
LanguageGuide/Generics.md:as discussed in <doc:Generics#Extensions-with-a-Generic-Where-Clause> below.
LanguageGuide/Generics.md:which is discussed in <doc:Generics#Associated-Types-with-a-Generic-Where-Clause> below.
LanguageGuide/Initialization.md:<doc:Initialization#Class-Inheritance-and-Initialization> below.
LanguageGuide/Initialization.md:as described in <doc:Initialization#Automatic-Initializer-Inheritance> below.
LanguageGuide/Initialization.md:> For more information, see <doc:Initialization#Automatic-Initializer-Inheritance> below.
LanguageGuide/StringsAndCharacters.md: (Unicode is discussed in <doc:StringsAndCharacters#Unicode> below)
ReferenceManual/Statements.md:<doc:Statements#Continue-Statement> below.
ReferenceManual/Statements.md:and is discussed in <doc:Statements#Break-Statement> below.
ReferenceManual/Statements.md:Control transfer statements are discussed in <doc:Statements#Control-Transfer-Statements> below.
ReferenceManual/Statements.md:see <doc:Statements#Fallthrough-Statement> below.
ReferenceManual/Statements.md:<doc:Statements#Continue-Statement> below.
```
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1570,6 +1623,11 @@ and can definitely be assumed to exist at every point thereafter.
The primary use of implicitly unwrapped optionals in Swift is during class initialization,
⬇️ Suggested change -The primary use of implicitly unwrapped optionals in Swift is during class initialization,
+Use implicitly unwrapped optionals in Swift when initializing a class,
Stet.
This isn't an instruction — most class initialization doesn't use IUOs — it's a statement of the motivation for this language feature to exist.
In TSPL.docc/LanguageGuide/TheBasics.md:
> +Trying to recover from an invalid state isn't possible ---
+because the assertion failed,
+you know that at least one piece of the program's data is invalid,
+but you don't know why it's invalid
+or what additional state is also invalid.
⬇️ Suggested change -Trying to recover from an invalid state isn't possible ---
-because the assertion failed,
-you know that at least one piece of the program's data is invalid,
-but you don't know why it's invalid
-or what additional state is also invalid.
+Recovering from an invalid state is impossible.
+When an assertion fails,
+at least one piece of the program's data is invalid ---
+but you don't know why it's invalid
+or whether an additional state is also invalid.
Changed — I think this rewrite works.
TSPL often uses the phrasing "trying to do X" when describing something that you might attempt, but which doesn't work.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1293,13 +1272,6 @@ var surveyAnswer: String?
```
-->
-> Note: Swift's `nil` isn't the same as `nil` in Objective-C.
-> In Objective-C, `nil` is a pointer to a nonexistent object.
-> In Swift, `nil` isn't a pointer --- it's the absence of a value of a certain type.
-> Optionals of *any* type can be set to `nil`, not just object types.
-
-### If Statements and Forced Unwrapping
-
You can use an `if` statement to find out whether an optional contains a value
by comparing the optional against `nil`.
You perform this comparison with the “equal to” operator (`==`)
⬇️ Suggested change -You perform this comparison with the “equal to” operator (`==`)
+You perform this comparison with the equal-to operator (`==`)
Suggest stet; this matches how operators are named throughout TSPL.
In TSPL.docc/LanguageGuide/TheBasics.md:
> @@ -1308,6 +1280,9 @@ or the “not equal to” operator (`!=`).
If an optional has a value, it's considered to be “not equal to” `nil`:
⬇️ Suggested change -If an optional has a value, it's considered to be “not equal to” `nil`:
+If an optional has a value, it's considered as not-equal-to, `nil`:
Suggest stet; this matches how operators are named throughout TSPL.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approved with changes as noted in Alex's latest commit:
Thank you for the detailed response. :)
nilvalue, and briefly introduce the??operator.Fixes: rdar://41014749
Forum (post-hoc) pitch: https://forums.swift.org/t/65744