From 54a8b5a75ac8114176d8e342a18bf399d429d775 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 17 May 2022 17:12:06 -0700 Subject: [PATCH 01/29] Start sketching reference for existential any. --- .../TSPL/TSPL.docc/ReferenceManual/Types.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index a2b0b8f67..aee70d374 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -45,6 +45,7 @@ type --> tuple-type type --> optional-type type --> implicitly-unwrapped-optional-type type --> protocol-composition-type +type --> boxed-protocol-type type --> opaque-type type --> metatype-type type --> any-type @@ -924,6 +925,43 @@ opaque-type --> ``some`` type ``` +## Boxed Protocol Type + +A *boxed protocol type* defines a type +that conforms to a protocol or protocol composition. + +Boxed protocol types have the following form: + +```swift +any <#constraint#> +``` + +The *constraint* is a protocol type, +protocol composition type, +a metatype of a protocol type, +or a metatype of a protocol composition type. + + + +Unlike an opaque type, +where there is some specific conforming type known at compile time, +any conforming type can be used at runtime. +To make this possible, +storage for an instance of a boxed protocol type +is allocated in a way that any conforming type can be stored. +The additional level of indirection needed to handle any conforming type +is called *boxing*. + +``` +Grammar of a boxed protocol type + +boxed-protocol-type --> ``any`` type +``` + + ## Metatype Type A *metatype type* refers to the type of any type, From 85540f119d1285cd77ab409d7a6cb908c58a065f Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 25 May 2022 12:17:43 -0700 Subject: [PATCH 02/29] Mention Any and AnyObject as related to 'any'. --- .../TSPL/TSPL.docc/ReferenceManual/Types.md | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index aee70d374..f12d73c6f 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -941,19 +941,48 @@ protocol composition type, a metatype of a protocol type, or a metatype of a protocol composition type. +At runtime, +an instance of a boxed protocol type can contain a value +of any type that satisfies the *constraint*. +This behavior contrasts with how an opaque types work, +where there is some specific conforming type known at compile time. +The additional level of indirection that's used +when working with a boxed protocol type is called :newTerm:`boxing`. +Applying ``any`` to the ``Any`` or ``AnyObject`` types +is redundant and has no effect. + + + -Unlike an opaque type, -where there is some specific conforming type known at compile time, -any conforming type can be used at runtime. -To make this possible, -storage for an instance of a boxed protocol type -is allocated in a way that any conforming type can be stored. -The additional level of indirection needed to handle any conforming type -is called *boxing*. + + ``` Grammar of a boxed protocol type From bb28dcea4820e2f71e1ac3e2b59dc30b0c0bd36e Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 27 Oct 2022 15:26:38 -0700 Subject: [PATCH 03/29] Add 'any' to code listings. --- .../TSPL/TSPL.docc/GuidedTour/GuidedTour.md | 4 +-- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 2 +- .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 26 ++++++++++++++++--- .../TSPL/TSPL.docc/ReferenceManual/Types.md | 11 ++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/GuidedTour/GuidedTour.md b/Sources/TSPL/TSPL.docc/GuidedTour/GuidedTour.md index 054567325..4897ba0f5 100644 --- a/Sources/TSPL/TSPL.docc/GuidedTour/GuidedTour.md +++ b/Sources/TSPL/TSPL.docc/GuidedTour/GuidedTour.md @@ -2009,11 +2009,11 @@ You can use a protocol name just like any other named type --- for example, to create a collection of objects that have different types but that all conform to a single protocol. -When you work with values whose type is a protocol type, +When you work with values whose type is a boxed protocol type, methods outside the protocol definition aren't available. ```swift -let protocolValue: ExampleProtocol = a +let protocolValue: any ExampleProtocol = a print(protocolValue.simpleDescription) // Prints "A very simple class. Now 100% adjusted." // print(protocolValue.anotherProperty) // Uncomment to see the error diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 624ae7956..bf4164185 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -10,7 +10,7 @@ Hiding type information is useful at boundaries between a module and code that calls into the module, because the underlying type of the return value can remain private. -Unlike returning a value whose type is a protocol type, +Unlike returning a value whose type is a boxed protocol type, opaque types preserve type identity --- the compiler has access to the type information, but clients of the module don't. diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md index 98481717e..9cfc9e1bb 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md @@ -764,6 +764,24 @@ a nonfailable initializer or an implicitly unwrapped failable initializer. ## Protocols as Types + + Protocols don't actually implement any functionality themselves. Nonetheless, you can use protocols as a fully fledged types in your code. @@ -788,8 +806,8 @@ Here's an example of a protocol used as a type: ```swift class Dice { let sides: Int - let generator: RandomNumberGenerator - init(sides: Int, generator: RandomNumberGenerator) { + let generator: any RandomNumberGenerator + init(sides: Int, generator: any RandomNumberGenerator) { self.sides = sides self.generator = generator } @@ -806,8 +824,8 @@ class Dice { ```swifttest -> class Dice { let sides: Int - let generator: RandomNumberGenerator - init(sides: Int, generator: RandomNumberGenerator) { + let generator: any RandomNumberGenerator + init(sides: Int, generator: any RandomNumberGenerator) { self.sides = sides self.generator = generator } diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index f12d73c6f..3f3186774 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -878,6 +878,11 @@ An *opaque type* defines a type that conforms to a protocol or protocol composition, without specifying the underlying concrete type. +◊ there is a specific concrete type +◊ the underlying type is known at compile time +◊ the compiler can use that type for optimizations +◊ but... the opaque type forms a boundary, which type information can't cross + Opaque types appear as the return type of a function or subscript, or the type of a property. Opaque types can't appear as part of a tuple type or a generic type, @@ -929,6 +934,12 @@ opaque-type --> ``some`` type A *boxed protocol type* defines a type that conforms to a protocol or protocol composition. + Boxed protocol types have the following form: From 1f8cee3bb92b2abc49ca9d1bec29eef1ee2b6f81 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 2 Nov 2022 09:13:12 -0700 Subject: [PATCH 04/29] Wording & spelling fix. --- Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md | 2 +- Sources/TSPL/TSPL.docc/ReferenceManual/Types.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index bf4164185..d2d641b32 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -493,7 +493,7 @@ the return value always has the same underlying type of `[T]`, so it follows the requirement that functions with opaque return types must return values of only a single type. -## Differences Between Opaque Types and Protocol Types +## Differences Between Opaque Types and Boxed Protocol Types Returning an opaque type looks very similar to using a protocol type as the return type of a function, diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index 3f3186774..a088522c0 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -936,7 +936,7 @@ A *boxed protocol type* defines a type that conforms to a protocol or protocol composition. From a17a3dd3a2983c1e8da496ea461848b4a8ae7429 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 7 Nov 2022 15:41:21 -0800 Subject: [PATCH 05/29] Move existentials out of Protocols chapter. --- .../LanguageGuide/BoxedProtocolTypes.md | 122 ++++++++++++++++++ .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 119 +---------------- Sources/TSPL/TSPL.docc/TSPL.md | 1 + 3 files changed, 124 insertions(+), 118 deletions(-) create mode 100644 Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md new file mode 100644 index 000000000..c15c74d96 --- /dev/null +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md @@ -0,0 +1,122 @@ +# Boxed Protocol Types + +Using a protocol as a type is sometimes called an *existential type*, +which comes from the phrase +"there exists a type *T* such that *T* conforms to the protocol". + +You can use a protocol in many places where other types are allowed, including: + +- As a parameter type or return type in a function, method, or initializer +- As the type of a constant, variable, or property +- As the type of items in an array, dictionary, or other container + +> Note: Because protocols are types, +> begin their names with a capital letter +> (such as `FullyNamed` and `RandomNumberGenerator`) +> to match the names of other types in Swift +> (such as `Int`, `String`, and `Double`). + +Here's an example of a protocol used as a type: + +```swift +class Dice { + let sides: Int + let generator: any RandomNumberGenerator + init(sides: Int, generator: any RandomNumberGenerator) { + self.sides = sides + self.generator = generator + } + func roll() -> Int { + return Int(generator.random() * Double(sides)) + 1 + } +} +``` + + +@Comment { + - test: `protocols` + + ```swifttest + -> class Dice { + let sides: Int + let generator: any RandomNumberGenerator + init(sides: Int, generator: any RandomNumberGenerator) { + self.sides = sides + self.generator = generator + } + func roll() -> Int { + return Int(generator.random() * Double(sides)) + 1 + } + } + ``` +} + +This example defines a new class called `Dice`, +which represents an *n*-sided dice for use in a board game. +`Dice` instances have an integer property called `sides`, +which represents how many sides they have, +and a property called `generator`, +which provides a random number generator +from which to create dice roll values. + +The `generator` property is of type `RandomNumberGenerator`. +Therefore, you can set it to an instance of +*any* type that adopts the `RandomNumberGenerator` protocol. +Nothing else is required of the instance you assign to this property, +except that the instance must adopt the `RandomNumberGenerator` protocol. +Because its type is `RandomNumberGenerator`, +code inside the `Dice` class can only interact with `generator` +in ways that apply to all generators that conform to this protocol. +That means it can't use any methods or properties +that are defined by the underlying type of the generator. +However, you can downcast from a protocol type to an underlying type +in the same way you can downcast from a superclass to a subclass, +as discussed in . + +`Dice` also has an initializer, to set up its initial state. +This initializer has a parameter called `generator`, +which is also of type `RandomNumberGenerator`. +You can pass a value of any conforming type in to this parameter +when initializing a new `Dice` instance. + +`Dice` provides one instance method, `roll`, +which returns an integer value between 1 and the number of sides on the dice. +This method calls the generator's `random()` method to create +a new random number between `0.0` and `1.0`, +and uses this random number to create a dice roll value within the correct range. +Because `generator` is known to adopt `RandomNumberGenerator`, +it's guaranteed to have a `random()` method to call. + +Here's how the `Dice` class can be used to create a six-sided dice +with a `LinearCongruentialGenerator` instance as its random number generator: + +```swift +var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) +for _ in 1...5 { + print("Random dice roll is \(d6.roll())") +} +// Random dice roll is 3 +// Random dice roll is 5 +// Random dice roll is 4 +// Random dice roll is 5 +// Random dice roll is 4 +``` + + +@Comment { + - test: `protocols` + + ```swifttest + -> var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) + -> for _ in 1...5 { + print("Random dice roll is \(d6.roll())") + } + Note: Because protocols are types, -> begin their names with a capital letter -> (such as `FullyNamed` and `RandomNumberGenerator`) -> to match the names of other types in Swift -> (such as `Int`, `String`, and `Double`). - -Here's an example of a protocol used as a type: - -```swift -class Dice { - let sides: Int - let generator: any RandomNumberGenerator - init(sides: Int, generator: any RandomNumberGenerator) { - self.sides = sides - self.generator = generator - } - func roll() -> Int { - return Int(generator.random() * Double(sides)) + 1 - } -} -``` - - -@Comment { - - test: `protocols` - - ```swifttest - -> class Dice { - let sides: Int - let generator: any RandomNumberGenerator - init(sides: Int, generator: any RandomNumberGenerator) { - self.sides = sides - self.generator = generator - } - func roll() -> Int { - return Int(generator.random() * Double(sides)) + 1 - } - } - ``` -} - -This example defines a new class called `Dice`, -which represents an *n*-sided dice for use in a board game. -`Dice` instances have an integer property called `sides`, -which represents how many sides they have, -and a property called `generator`, -which provides a random number generator -from which to create dice roll values. - -The `generator` property is of type `RandomNumberGenerator`. -Therefore, you can set it to an instance of -*any* type that adopts the `RandomNumberGenerator` protocol. -Nothing else is required of the instance you assign to this property, -except that the instance must adopt the `RandomNumberGenerator` protocol. -Because its type is `RandomNumberGenerator`, -code inside the `Dice` class can only interact with `generator` -in ways that apply to all generators that conform to this protocol. -That means it can't use any methods or properties -that are defined by the underlying type of the generator. -However, you can downcast from a protocol type to an underlying type -in the same way you can downcast from a superclass to a subclass, -as discussed in . - -`Dice` also has an initializer, to set up its initial state. -This initializer has a parameter called `generator`, -which is also of type `RandomNumberGenerator`. -You can pass a value of any conforming type in to this parameter -when initializing a new `Dice` instance. - -`Dice` provides one instance method, `roll`, -which returns an integer value between 1 and the number of sides on the dice. -This method calls the generator's `random()` method to create -a new random number between `0.0` and `1.0`, -and uses this random number to create a dice roll value within the correct range. -Because `generator` is known to adopt `RandomNumberGenerator`, -it's guaranteed to have a `random()` method to call. - -Here's how the `Dice` class can be used to create a six-sided dice -with a `LinearCongruentialGenerator` instance as its random number generator: - -```swift -var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) -for _ in 1...5 { - print("Random dice roll is \(d6.roll())") -} -// Random dice roll is 3 -// Random dice roll is 5 -// Random dice roll is 4 -// Random dice roll is 5 -// Random dice roll is 4 -``` - - -@Comment { - - test: `protocols` - - ```swifttest - -> var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) - -> for _ in 1...5 { - print("Random dice roll is \(d6.roll())") - } - - - +- - - - From 851b86d5db46dd4f6f1c2dd4da5e9ecfc2183900 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 7 Nov 2022 15:44:54 -0800 Subject: [PATCH 06/29] Add forward references for "protocol as type". --- .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 43 ++++++++++++++++--- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md index 472137f0a..17471e216 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md @@ -764,12 +764,44 @@ a nonfailable initializer or an implicitly unwrapped failable initializer. ## Protocols as Types +Protocols don't actually implement any functionality themselves. +Nonetheless, you can use a protocol as a type in your code. + +The most common way to use a protocol as a type +is to use a protocol as generic constraint. +Code with generic constraints can work with +any type that conforms to the protocol, +and the specific type is chosen by the code that uses the API. +For example, +when you call a function that takes an argument +and that argument's type is generic, +the caller chooses the type. +For information about using protocols as generic constraints, +see . + +Code with an opaque type +works with some type that conforms to the protocol. +The underlying type is known at compile time, +and the API implementation chooses that type, +but that type's identity is hidden from clients of the API. +Using an opaque type lets you prevent implementation details of an API +from leaking through the layer of abstraction -- +for example, by hiding the specific return type from a function, +and only guaranteeing that the value conforms to a given protocol. +For information about opaque types, +see . + + + +Code with a boxed protocol type +works with any type, chosen at runtime, that conforms to the protocol. +To support this runtime flexibility, +Swift adds a level of indirection (known as a box), +which has a performance cost. +For information about boxed protocol types, +see . -Protocols don't actually implement any functionality themselves. -Nonetheless, -you can use protocols as a fully fledged types in your code. ## Delegation From 87638d7838d13a41a9bfaa394eb627aa26fac909 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 7 Nov 2022 15:55:29 -0800 Subject: [PATCH 07/29] Mark some places I need to come back to. --- .../TSPL.docc/LanguageGuide/BoxedProtocolTypes.md | 12 ++++++++++++ Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md index c15c74d96..a876ce685 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md @@ -1,9 +1,21 @@ # Boxed Protocol Types +XXX chapter abstract goes here + Using a protocol as a type is sometimes called an *existential type*, which comes from the phrase "there exists a type *T* such that *T* conforms to the protocol". + + You can use a protocol in many places where other types are allowed, including: - As a parameter type or return type in a function, method, or initializer diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index d2d641b32..235eac854 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -15,6 +15,8 @@ opaque types preserve type identity --- the compiler has access to the type information, but clients of the module don't. + + ## The Problem That Opaque Types Solve For example, @@ -495,6 +497,8 @@ must return values of only a single type. ## Differences Between Opaque Types and Boxed Protocol Types + + Returning an opaque type looks very similar to using a protocol type as the return type of a function, but these two kinds of return type differ in From 4112fde93fa3104df86b6e2753b054dc9e7f1652 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Fri, 18 Nov 2022 10:55:56 -0800 Subject: [PATCH 08/29] Move existentials in with opaque types. This effectively reverts commit a17a3dd3 and takes a different approach. --- .../LanguageGuide/BoxedProtocolTypes.md | 131 ----------------- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 134 +++++++++++++++++- .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 11 +- Sources/TSPL/TSPL.docc/TSPL.md | 1 - 4 files changed, 138 insertions(+), 139 deletions(-) delete mode 100644 Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md deleted file mode 100644 index 486105fc7..000000000 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/BoxedProtocolTypes.md +++ /dev/null @@ -1,131 +0,0 @@ -# Boxed Protocol Types - -XXX chapter abstract goes here - -Using a protocol as a type is sometimes called an *existential type*, -which comes from the phrase -"there exists a type *T* such that *T* conforms to the protocol". - - - -You can use a protocol in many places where other types are allowed, including: - -- As a parameter type or return type in a function, method, or initializer -- As the type of a constant, variable, or property -- As the type of items in an array, dictionary, or other container - -> Note: Because protocols are types, -> begin their names with a capital letter -> (such as `FullyNamed` and `RandomNumberGenerator`) -> to match the names of other types in Swift -> (such as `Int`, `String`, and `Double`). - -Here's an example of a protocol used as a type: - -```swift -class Dice { - let sides: Int - let generator: any RandomNumberGenerator - init(sides: Int, generator: any RandomNumberGenerator) { - self.sides = sides - self.generator = generator - } - func roll() -> Int { - return Int(generator.random() * Double(sides)) + 1 - } -} -``` - - - -This example defines a new class called `Dice`, -which represents an *n*-sided dice for use in a board game. -`Dice` instances have an integer property called `sides`, -which represents how many sides they have, -and a property called `generator`, -which provides a random number generator -from which to create dice roll values. - -The `generator` property is of type `RandomNumberGenerator`. -Therefore, you can set it to an instance of -*any* type that adopts the `RandomNumberGenerator` protocol. -Nothing else is required of the instance you assign to this property, -except that the instance must adopt the `RandomNumberGenerator` protocol. -Because its type is `RandomNumberGenerator`, -code inside the `Dice` class can only interact with `generator` -in ways that apply to all generators that conform to this protocol. -That means it can't use any methods or properties -that are defined by the underlying type of the generator. -However, you can downcast from a protocol type to an underlying type -in the same way you can downcast from a superclass to a subclass, -as discussed in . - -`Dice` also has an initializer, to set up its initial state. -This initializer has a parameter called `generator`, -which is also of type `RandomNumberGenerator`. -You can pass a value of any conforming type in to this parameter -when initializing a new `Dice` instance. - -`Dice` provides one instance method, `roll`, -which returns an integer value between 1 and the number of sides on the dice. -This method calls the generator's `random()` method to create -a new random number between `0.0` and `1.0`, -and uses this random number to create a dice roll value within the correct range. -Because `generator` is known to adopt `RandomNumberGenerator`, -it's guaranteed to have a `random()` method to call. - -Here's how the `Dice` class can be used to create a six-sided dice -with a `LinearCongruentialGenerator` instance as its random number generator: - -```swift -var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) -for _ in 1...5 { - print("Random dice roll is \(d6.roll())") -} -// Random dice roll is 3 -// Random dice roll is 5 -// Random dice roll is 4 -// Random dice roll is 5 -// Random dice roll is 4 -``` - - - diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 29df87834..27ed231aa 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -1,4 +1,4 @@ -# Opaque Types +# Opaque and Boxed Types Hide implementation details about a value's type. @@ -17,6 +17,8 @@ but clients of the module don't. + + ## The Problem That Opaque Types Solve For example, @@ -486,6 +488,135 @@ the return value always has the same underlying type of `[T]`, so it follows the requirement that functions with opaque return types must return values of only a single type. +## Boxed Protocol Types + +Using a protocol as a type is sometimes called an *existential type*, +which comes from the phrase +"there exists a type *T* such that *T* conforms to the protocol". + + + +You can use a protocol in many places where other types are allowed, including: + +- As a parameter type or return type in a function, method, or initializer +- As the type of a constant, variable, or property +- As the type of items in an array, dictionary, or other container + +> Note: Because protocols are types, +> begin their names with a capital letter +> (such as `FullyNamed` and `RandomNumberGenerator`) +> to match the names of other types in Swift +> (such as `Int`, `String`, and `Double`). + +Here's an example of a protocol used as a type: + +```swift +class Dice { + let sides: Int + let generator: any RandomNumberGenerator + init(sides: Int, generator: any RandomNumberGenerator) { + self.sides = sides + self.generator = generator + } + func roll() -> Int { + return Int(generator.random() * Double(sides)) + 1 + } +} +``` + + + +This example defines a new class called `Dice`, +which represents an *n*-sided dice for use in a board game. +`Dice` instances have an integer property called `sides`, +which represents how many sides they have, +and a property called `generator`, +which provides a random number generator +from which to create dice roll values. + +The `generator` property is of type `RandomNumberGenerator`. +Therefore, you can set it to an instance of +*any* type that adopts the `RandomNumberGenerator` protocol. +Nothing else is required of the instance you assign to this property, +except that the instance must adopt the `RandomNumberGenerator` protocol. +Because its type is `RandomNumberGenerator`, +code inside the `Dice` class can only interact with `generator` +in ways that apply to all generators that conform to this protocol. +That means it can't use any methods or properties +that are defined by the underlying type of the generator. +However, you can downcast from a protocol type to an underlying type +in the same way you can downcast from a superclass to a subclass, +as discussed in . + +`Dice` also has an initializer, to set up its initial state. +This initializer has a parameter called `generator`, +which is also of type `RandomNumberGenerator`. +You can pass a value of any conforming type in to this parameter +when initializing a new `Dice` instance. + +`Dice` provides one instance method, `roll`, +which returns an integer value between 1 and the number of sides on the dice. +This method calls the generator's `random()` method to create +a new random number between `0.0` and `1.0`, +and uses this random number to create a dice roll value within the correct range. +Because `generator` is known to adopt `RandomNumberGenerator`, +it's guaranteed to have a `random()` method to call. + +Here's how the `Dice` class can be used to create a six-sided dice +with a `LinearCongruentialGenerator` instance as its random number generator: + +```swift +var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) +for _ in 1...5 { + print("Random dice roll is \(d6.roll())") +} +// Random dice roll is 3 +// Random dice roll is 5 +// Random dice roll is 4 +// Random dice roll is 5 +// Random dice roll is 4 +``` + + + ## Differences Between Opaque Types and Boxed Protocol Types @@ -797,3 +928,4 @@ Licensed under Apache License v2.0 with Runtime Library Exception See https://swift.org/LICENSE.txt for license information See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors --> + diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md index 89209e556..ca5d372c3 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md @@ -760,8 +760,6 @@ For example, when you call a function that takes an argument and that argument's type is generic, the caller chooses the type. -For information about using protocols as generic constraints, -see . Code with an opaque type works with some type that conforms to the protocol. @@ -772,8 +770,6 @@ Using an opaque type lets you prevent implementation details of an API from leaking through the layer of abstraction -- for example, by hiding the specific return type from a function, and only guaranteeing that the value conforms to a given protocol. -For information about opaque types, -see . @@ -782,8 +778,11 @@ works with any type, chosen at runtime, that conforms to the protocol. To support this runtime flexibility, Swift adds a level of indirection (known as a box), which has a performance cost. -For information about boxed protocol types, -see . + +For information about using protocols as generic constraints, +see . +For information about opaque types, and boxed protocol types, +see . - ## The Problem That Opaque Types Solve @@ -290,6 +288,8 @@ but the function could be rewritten to draw a trapezoid in a variety of other ways without changing its return type. + + This example highlights the way that an opaque return type is like the reverse of a generic type. The code inside `makeTrapezoid()` can return any type it needs to, @@ -619,8 +619,6 @@ for _ in 1...5 { ## Differences Between Opaque Types and Boxed Protocol Types - - Returning an opaque type looks very similar to using a protocol type as the return type of a function, but these two kinds of return type differ in From edd9b60ffabb8c1b414d568280cb045c030bc44c Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Tue, 29 Nov 2022 20:38:20 -0800 Subject: [PATCH 10/29] Revise outline. Incorporates feedback from Xiaodi Wu (@xwu) from the pitch thread. --- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 50 +++++++++++++++---- .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 15 +++++- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 5843d1e69..3ddb73553 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -500,21 +500,51 @@ XXX OUTLINE - explain how they are "boxed" - introduce the "any" keyword +- list example(s) of why you would use boxed protocol type + e.g. heterogeneous array - revisit the list of where you can use existentials ---> +The existing dice and random numbers example +isn't really a good illustration of using existentials. +It would probably be better expressed as Dice instead; +through the whole lifetime of a Dice instance, +it only has a single type generating random numbers. + +Xiodi Wu wrote: + + Rather, in terms of user-facing language semantics, I'd throw in a mention + there (even a short one) that because the underlying type can vary at + runtime, there isn't a statically known underlying type, and for that + reason not all APIs (nor protocol conformances) of the underlying type can + be made available through the box. It would be really helpful if then in + the corresponding dedicated subsection on existentials some more detailed + but still didactically appropriate treatment of this issue could be + included, as it is a very common question here on the user forums and + probably more of what users trip up on when they use existentials than the + performance bit. (Feel free to lift any text I've contributed to the + educational notes in the Swift repository on this issue.) + +You can as-cast an existential, +like other values of non-concrete types or subtypes, +if you know the underlying type: + + protocol P { + var p: Int { get } + } + + struct S: P { + var p = 12 + var q = 100 + } + + var s: any P = S() + print(s.p) -You can use a protocol in many places where other types are allowed, including: + if let ss = s as? S { print(ss.q) } -- As a parameter type or return type in a function, method, or initializer -- As the type of a constant, variable, or property -- As the type of items in an array, dictionary, or other container + print(type(of: s)) -> Note: Because protocols are types, -> begin their names with a capital letter -> (such as `FullyNamed` and `RandomNumberGenerator`) -> to match the names of other types in Swift -> (such as `Int`, `String`, and `Double`). +--> Here's an example of a protocol used as a type: diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md index ca5d372c3..eff9ded79 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md @@ -88,6 +88,12 @@ class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { ``` --> +> Note: Because protocols are types, +> begin their names with a capital letter +> (such as `FullyNamed` and `RandomNumberGenerator`) +> to match the names of other types in Swift +> (such as `Int`, `String`, and `Double`). + ## Property Requirements A protocol can require any conforming type to provide @@ -776,8 +782,13 @@ and only guaranteeing that the value conforms to a given protocol. Code with a boxed protocol type works with any type, chosen at runtime, that conforms to the protocol. To support this runtime flexibility, -Swift adds a level of indirection (known as a box), -which has a performance cost. +Swift adds a level of indirection when necessary --- +this indirection is known as a *box*, +and it has a performance cost. +Because of this flexibility, +Swift doesn't know the underlying type until the code is running, +which means only protocol requirements are available, +not any other APIs from the underlying type. For information about using protocols as generic constraints, see . From 462d18a3fc9a984d183f3b3cdb4e4d5d866abf34 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 30 Nov 2022 11:34:38 -0800 Subject: [PATCH 11/29] Rough in a better example of existentials. --- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 85 ++++++++++++++----- .../TSPL/TSPL.docc/LanguageGuide/Protocols.md | 8 +- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 3ddb73553..7dbcace90 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -549,37 +549,78 @@ if you know the underlying type: Here's an example of a protocol used as a type: ```swift -class Dice { - let sides: Int - let generator: any RandomNumberGenerator - init(sides: Int, generator: any RandomNumberGenerator) { - self.sides = sides - self.generator = generator - } - func roll() -> Int { - return Int(generator.random() * Double(sides)) + 1 - } +func drawVertically(_ shapes: any Shape...) { + for shape in shapes { + print(shape.draw()) + print() + } } + +let largeTriangle = Triangle(size: 5) +let largeSquare = Square(size: 5) +drawVertically(largeTriangle, largeSquare) ``` +In the example above, +`shapes` is an heterogeneous array: its values have different types, +but they all conform to the `Shape` protocol. +Within the `drawVertically(_:)` function, +the code can use methods, properties, and subscripts +that are required by the `Shape` protocol --- +for example, this code calls the `draw()` method on each element of the array. + + + This example defines a new class called `Dice`, which represents an *n*-sided dice for use in a board game. `Dice` instances have an integer property called `sides`, diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md index eff9ded79..72c8f7d4b 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/Protocols.md @@ -786,9 +786,11 @@ Swift adds a level of indirection when necessary --- this indirection is known as a *box*, and it has a performance cost. Because of this flexibility, -Swift doesn't know the underlying type until the code is running, -which means only protocol requirements are available, -not any other APIs from the underlying type. +Swift doesn't know the underlying type at compile time, +which means you can access only the members +that are required by the protocol. +Accessing any other APIs on the underlying type +requires casting at runtime. For information about using protocols as generic constraints, see . From 859e88ab85ac25af269eb66e214bb4118fce689f Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 30 Nov 2022 17:00:46 -0800 Subject: [PATCH 12/29] Fix markup for code voice. --- Sources/TSPL/TSPL.docc/ReferenceManual/Types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index fa7ef6b42..9c32c7e01 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -932,7 +932,7 @@ This behavior contrasts with how an opaque types work, where there is some specific conforming type known at compile time. The additional level of indirection that's used when working with a boxed protocol type is called :newTerm:`boxing`. -Applying ``any`` to the ``Any`` or ``AnyObject`` types +Applying `any` to the `Any` or `AnyObject` types is redundant and has no effect. +A boxed protocol type can store an instance of any type +that conforms to the given protocol. +Boxed protocol types don't preserve type identity --- +the value's specific type isn't known until runtime, +and can change over time as different values are stored. ## The Problem That Opaque Types Solve From ac1da69e0f3d3a9fc70a21e7de2d3293bfc6e0c9 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 1 Dec 2022 11:44:22 -0800 Subject: [PATCH 14/29] Expand the code listing and its discussion. Avoiding a variadic function arguments keeps the syntax a little simpler, and using the array of boxed Shape types in a wrapper type matches the flow of the other examples in the chapter. --- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 105 +++++++++++------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 347f493f7..be1ce12f1 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -496,26 +496,8 @@ must return values of only a single type. ## Boxed Protocol Types -Using a protocol as a type is sometimes called an *existential type*, -which comes from the phrase -"there exists a type *T* such that *T* conforms to the protocol". - -Here's an example of a protocol used as a type: +A boxed protocol type is also sometimes called an *existential type*, +which comes from the phrase +"there exists a type *T* such that *T* conforms to the protocol". +To make a boxed protocol type, +write `any` before the name of a protocol. +Here's an example: ```swift -func drawVertically(_ shapes: any Shape...) { - for shape in shapes { - print(shape.draw()) - print() +struct VerticalShapes: Shape { + var shapes: [any Shape] + func draw() -> String { + return shapes.map { $0.draw() }.joined(separator: "\n\n") } } let largeTriangle = Triangle(size: 5) let largeSquare = Square(size: 5) -drawVertically(largeTriangle, largeSquare) +let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare]) +print(vertical.draw()) ``` -In the example above, -`shapes` is an heterogeneous array: its values have different types, -but they all conform to the `Shape` protocol. -Within the `drawVertically(_:)` function, -the code can use methods, properties, and subscripts -that are required by the `Shape` protocol --- -for example, this code calls the `draw()` method on each element of the array. - - +In the example above, +`VerticalShapes` declares the type of `shapes` as `[any Shape]` --- +an array of boxed `Shape` elements. +Each element in the array can be a different type, +and each of those types must conform to the `Shape` protocol. + +Within the `VerticalShapes` type, +the code can use methods, properties, and subscripts +that are required by the `Shape` protocol. +The `draw()` method of `VerticalShapes` +calls the `draw()` method on each element of the array. +This API is available because `Shape` requires a `draw()` method. +In contrast, +trying to access the `size` property of the triangle, +or any other APIs that aren't required by `Shape`, +produces an error. + +Contrast the three types you could use for `shapes`: + +- Using generics, + by writing `struct VerticalShapes` and `var shapes: [S]`, + makes an array whose elements are some specific shape type, + and where the identity of that specific type + is visible to any code that interacts with the array. + +- Using an opaque type, + by writing `var shapes: [some Shape]`, + makes an array whose elements are some specific shape type, + and where that specific type's identify is hidden. + +- Using a boxed protocol type, + by writing `var shapes: [any Shape]`, + makes an array that can store elements of different types, + and where those types' identities are hidden. + +In this case, +a boxed protocol type is the only approach +that lets callers of `VerticalShapes` mix different kinds of shapes together. + + + + + + +At compile time, +a value whose type is opaque has a specific concrete type, +and Swift can use that underlying type for optimizations. +However, +the opaque type forms a boundary +that information about that underlying type can't cross. + Protocol declarations can't include opaque types. Classes can't use an opaque type as the return type of a nonfinal method. @@ -906,13 +908,9 @@ could return a value of type `T` or `Dictionary`. ## Boxed Protocol Type A *boxed protocol type* defines a type -that conforms to a protocol or protocol composition. - +that conforms to a protocol or protocol composition, +with the ability for that conforming type +to vary while the program is running. Boxed protocol types have the following form: @@ -932,6 +930,9 @@ This behavior contrasts with how an opaque types work, where there is some specific conforming type known at compile time. The additional level of indirection that's used when working with a boxed protocol type is called :newTerm:`boxing`. +Boxing typically requires a separate memory allocation for storage +and an additional level of indirection for access. + Applying `any` to the `Any` or `AnyObject` types is redundant and has no effect. From bbb5b19755e82606574434037b7a603b71af4301 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 20 Feb 2023 16:25:45 -0800 Subject: [PATCH 18/29] Remove old example. Confirmed that the replacement text illustrates the same functionality. --- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 71 ------------------- 1 file changed, 71 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index ed3cd3ec3..93bf22782 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -653,77 +653,6 @@ Memory allocation for the box adds a level of indirection. - - - - ## Differences Between Opaque Types and Boxed Protocol Types Returning an opaque type looks very similar From ecbe6c527d76f5227e833817e72bef6dba5f4caf Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 20 Feb 2023 16:44:38 -0800 Subject: [PATCH 19/29] Introduce "box" earlier and clean up phrasing. --- .../TSPL.docc/LanguageGuide/OpaqueTypes.md | 21 +++++++------------ .../TSPL/TSPL.docc/ReferenceManual/Types.md | 3 ++- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md index 93bf22782..def17fbfa 100644 --- a/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/Sources/TSPL/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -294,8 +294,6 @@ but the function could be rewritten to draw a trapezoid in a variety of other ways without changing its return type. - - This example highlights the way that an opaque return type is like the reverse of a generic type. The code inside `makeTrapezoid()` can return any type it needs to, @@ -610,16 +608,20 @@ In the example above, an array of boxed `Shape` elements. Each element in the array can be a different type, and each of those types must conform to the `Shape` protocol. +To support this runtime flexibility, +Swift adds a level of indirection when necessary --- +this indirection is called a *box*, +and it has a performance cost. Within the `VerticalShapes` type, the code can use methods, properties, and subscripts that are required by the `Shape` protocol. -The `draw()` method of `VerticalShapes` +For example, the `draw()` method of `VerticalShapes` calls the `draw()` method on each element of the array. -This API is available because `Shape` requires a `draw()` method. +This method is available because `Shape` requires a `draw()` method. In contrast, trying to access the `size` property of the triangle, -or any other APIs that aren't required by `Shape`, +or any other properties or methods that aren't required by `Shape`, produces an error. Contrast the three types you could use for `shapes`: @@ -644,15 +646,6 @@ In this case, a boxed protocol type is the only approach that lets callers of `VerticalShapes` mix different kinds of shapes together. -To support this runtime flexibility, -Swift adds a level of indirection when necessary --- -this indirection is known as a *box*, -and it has a performance cost. -Memory allocation for the box adds a level of indirection. - - - - ## Differences Between Opaque Types and Boxed Protocol Types Returning an opaque type looks very similar diff --git a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md index 1999c50ed..eeb029982 100644 --- a/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md +++ b/Sources/TSPL/TSPL.docc/ReferenceManual/Types.md @@ -931,7 +931,8 @@ where there is some specific conforming type known at compile time. The additional level of indirection that's used when working with a boxed protocol type is called :newTerm:`boxing`. Boxing typically requires a separate memory allocation for storage -and an additional level of indirection for access. +and an additional level of indirection for access, +which incurs a performance cost at runtime. Applying `any` to the `Any` or `AnyObject` types is redundant and has no effect. From f7c0c8181dda3ab2ec9c8a9dd32ddf9d6bdd74e0 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Wed, 22 Feb 2023 14:12:16 -0800 Subject: [PATCH 20/29] Add an example of as-casting an existential. --- TSPL.docc/LanguageGuide/OpaqueTypes.md | 51 +++++++------------------- 1 file changed, 13 insertions(+), 38 deletions(-) diff --git a/TSPL.docc/LanguageGuide/OpaqueTypes.md b/TSPL.docc/LanguageGuide/OpaqueTypes.md index c123dadb3..0dbe05711 100644 --- a/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -494,44 +494,6 @@ must return values of only a single type. ## Boxed Protocol Types - - A boxed protocol type is also sometimes called an *existential type*, which comes from the phrase "there exists a type *T* such that *T* conforms to the protocol". @@ -646,6 +608,19 @@ In this case, a boxed protocol type is the only approach that lets callers of `VerticalShapes` mix different kinds of shapes together. +You can use an `as` cast +when you know the underlying type of a boxed value. +For example: + +```swift +if let downcastTriangle = vertical.shapes[0] as? Triangle { + print(downcastTriangle.size) +} +// Prints "5" +``` + +For more information, see . + ## Differences Between Opaque Types and Boxed Protocol Types Returning an opaque type looks very similar From af9a7095c7c6fe366f07ae1fae9db386f5b909ff Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 23 Feb 2023 13:47:09 -0800 Subject: [PATCH 21/29] Minor wording adjustments from review. Co-authored-by: Xiaodi Wu <13952+xwu@users.noreply.github.com> Co-authored-by: Ben Rimmington --- TSPL.docc/LanguageGuide/OpaqueTypes.md | 2 +- TSPL.docc/LanguageGuide/Protocols.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TSPL.docc/LanguageGuide/OpaqueTypes.md b/TSPL.docc/LanguageGuide/OpaqueTypes.md index 0dbe05711..646f59160 100644 --- a/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -21,7 +21,7 @@ A boxed protocol type can store an instance of any type that conforms to the given protocol. Boxed protocol types don't preserve type identity --- the value's specific type isn't known until runtime, -and can change over time as different values are stored. +and it can change over time as different values are stored. ## The Problem That Opaque Types Solve diff --git a/TSPL.docc/LanguageGuide/Protocols.md b/TSPL.docc/LanguageGuide/Protocols.md index 3c156e241..cc463da7a 100644 --- a/TSPL.docc/LanguageGuide/Protocols.md +++ b/TSPL.docc/LanguageGuide/Protocols.md @@ -758,7 +758,7 @@ Protocols don't actually implement any functionality themselves. Nonetheless, you can use a protocol as a type in your code. The most common way to use a protocol as a type -is to use a protocol as generic constraint. +is to use a protocol as a generic constraint. Code with generic constraints can work with any type that conforms to the protocol, and the specific type is chosen by the code that uses the API. From 212dbdf718513bbfa5ed6270ee21e0f7a660949f Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 23 Feb 2023 13:43:37 -0800 Subject: [PATCH 22/29] Fix a link. DocC link syntax follows the destination title, not its filename. The chapter part is in UpperCamelCase and the heading is in Upper-Kebab-Case. --- TSPL.docc/LanguageGuide/OpaqueTypes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TSPL.docc/LanguageGuide/OpaqueTypes.md b/TSPL.docc/LanguageGuide/OpaqueTypes.md index 646f59160..cf342636d 100644 --- a/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -619,7 +619,7 @@ if let downcastTriangle = vertical.shapes[0] as? Triangle { // Prints "5" ``` -For more information, see . +For more information, see . ## Differences Between Opaque Types and Boxed Protocol Types From 203b915fc5617a4f7513f4e23f4723747e7b642d Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 23 Feb 2023 13:50:59 -0800 Subject: [PATCH 23/29] Use proper markup for new grammar production. --- TSPL.docc/ReferenceManual/Types.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/TSPL.docc/ReferenceManual/Types.md b/TSPL.docc/ReferenceManual/Types.md index bd93e90f8..f724e958d 100644 --- a/TSPL.docc/ReferenceManual/Types.md +++ b/TSPL.docc/ReferenceManual/Types.md @@ -968,13 +968,9 @@ Contrast P.Type with (any P.Type) and (any P).Type https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md#metatypes --> - -``` -Grammar of a boxed protocol type - -boxed-protocol-type --> ``any`` type -``` - +> Grammar of a boxed protocol type: +> +> *boxed-protocol-type* → **`any`** *type* ## Metatype Type From f808c8ff9db7d8241c051ec02b666847bf9bc37f Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 23 Feb 2023 13:54:58 -0800 Subject: [PATCH 24/29] Fix em-dash. --- TSPL.docc/LanguageGuide/Protocols.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TSPL.docc/LanguageGuide/Protocols.md b/TSPL.docc/LanguageGuide/Protocols.md index cc463da7a..a2b534f00 100644 --- a/TSPL.docc/LanguageGuide/Protocols.md +++ b/TSPL.docc/LanguageGuide/Protocols.md @@ -773,7 +773,7 @@ The underlying type is known at compile time, and the API implementation chooses that type, but that type's identity is hidden from clients of the API. Using an opaque type lets you prevent implementation details of an API -from leaking through the layer of abstraction -- +from leaking through the layer of abstraction --- for example, by hiding the specific return type from a function, and only guaranteeing that the value conforms to a given protocol. From 71108e59a9d0bbf94a9879f5c954d21d50e88493 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Thu, 23 Feb 2023 14:09:41 -0800 Subject: [PATCH 25/29] Add boxed-protocol-type to the grammar summary. --- TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md index 1c9d19aa2..adcec717f 100644 --- a/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md +++ b/TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md @@ -481,6 +481,10 @@ make the same change here also. > > *opaque-type* → **`some`** *type* +> Grammar of a boxed protocol type: +> +> *boxed-protocol-type* → **`any`** *type* + > Grammar of a metatype type: > > *metatype-type* → *type* **`.`** **`Type`** | *type* **`.`** **`Protocol`** From f40066e0ad0fd81e1ec872734bbf5eab61ce6143 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 20 Mar 2023 14:23:30 -0700 Subject: [PATCH 26/29] Any and AnyObject are already boxed. --- TSPL.docc/ReferenceManual/Types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TSPL.docc/ReferenceManual/Types.md b/TSPL.docc/ReferenceManual/Types.md index f724e958d..0e9f0f8ce 100644 --- a/TSPL.docc/ReferenceManual/Types.md +++ b/TSPL.docc/ReferenceManual/Types.md @@ -934,7 +934,8 @@ and an additional level of indirection for access, which incurs a performance cost at runtime. Applying `any` to the `Any` or `AnyObject` types -is redundant and has no effect. +has no effect, +because those types are already boxed protocol types. - Code with a boxed protocol type works with any type, chosen at runtime, that conforms to the protocol. To support this runtime flexibility, From bd69a4c07f70e01b3f32ce05bc1421a5fd181b97 Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 20 Mar 2023 14:30:21 -0700 Subject: [PATCH 28/29] Remove stray whitespace. --- TSPL.docc/LanguageGuide/OpaqueTypes.md | 2 +- TSPL.docc/ReferenceManual/SummaryOfTheGrammar.md | 2 +- TSPL.docc/ReferenceManual/Types.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TSPL.docc/LanguageGuide/OpaqueTypes.md b/TSPL.docc/LanguageGuide/OpaqueTypes.md index cf342636d..71dcd297e 100644 --- a/TSPL.docc/LanguageGuide/OpaqueTypes.md +++ b/TSPL.docc/LanguageGuide/OpaqueTypes.md @@ -517,7 +517,7 @@ print(vertical.draw()) - + > Grammar of a boxed protocol type: -> +> > *boxed-protocol-type* → **`any`** *type* ## Metatype Type From 4754b426914fefb8e227d8c3efbda060d2a6f8de Mon Sep 17 00:00:00 2001 From: Alex Martini Date: Mon, 1 May 2023 13:29:19 -0700 Subject: [PATCH 29/29] Incorporate edits. Co-authored-by: Chuck Toporek --- TSPL.docc/LanguageGuide/Protocols.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TSPL.docc/LanguageGuide/Protocols.md b/TSPL.docc/LanguageGuide/Protocols.md index c6323b4c6..ff68c5c6e 100644 --- a/TSPL.docc/LanguageGuide/Protocols.md +++ b/TSPL.docc/LanguageGuide/Protocols.md @@ -755,7 +755,7 @@ a nonfailable initializer or an implicitly unwrapped failable initializer. ## Protocols as Types Protocols don't actually implement any functionality themselves. -Nonetheless, you can use a protocol as a type in your code. +Regardless, you can use a protocol as a type in your code. The most common way to use a protocol as a type is to use a protocol as a generic constraint. @@ -781,8 +781,8 @@ Code with a boxed protocol type works with any type, chosen at runtime, that conforms to the protocol. To support this runtime flexibility, Swift adds a level of indirection when necessary --- -this indirection is known as a *box*, -and it has a performance cost. +known as a *box*, +which has a performance cost. Because of this flexibility, Swift doesn't know the underlying type at compile time, which means you can access only the members