For the purpose of this talk, imagine to have an
Animalprotocol
Generics are here to abstract away the details of a specific type
if you find yourself writing overloads with repetitive implementations, it might be a sign to that you need to generalize
Start with concrete types, generalize when needed
Polymorphism
ability of abstract code to behave differently for different concrete types
allows one piece of code to have many behaviors depending on how the code is used
Different forms:
ad-hoc polymorphism
the same function call can mean different things depending on the argument type
Swift function overloading
subtype polymorphism
code operating on a supertype can have different behavior based on the specific subtype the code is using at runtime
Subclassing in Swift, where a class
overrides a method of their superclass
parametric polymorphism - achieved using generics
Generic code uses type parameters to allow writing one piece of code that works with different types, and concrete types themselves are used as arguments
protocol
interface that represents capabilities
separates ideas from implementation details
abstraction tool that describes the functionality of conforming types
separates ideas from implementation details
each capability maps to a protocol requirement
the name of the protocol should represent the category of types we’re describing
associatedtype
serves as a placeholder for a concrete type
associated types depend on the specific type that conforms to the protocol
Opaque type vs. underlying type
Opaque type - abstract type that represents a placeholder for a specific concrete type
underlying type - specific concrete type that is substituted in the opaque type
For values with opaque type, the underlying type is fixed for the scope of the value
generic code using the value is guaranteed to get the same underlying type each time the value is accessed
opaque type can be used both for inputs and outputs
Opaque types in Swift:
some Animal<T: Animal>
Tips on writing generic interfaces (protocols or not)
some
Swift 5.6 and earlier:
func feed<A>(_ animal: A) where A: Animal
// 👆🏻👇🏻 Equivalents
func feed<A: Animal>(_ animal: A)Swift 5.7 and later:
func feed(_ animal: some Animal)we can express an abstract type in terms of the protocol conformance by writing
some xxxall three declarations above are identical, but the newer one is much easier to read and understand
the
somekeyword can be used in parameter and result typeswith
some, there is a specific underlying type that cannot vary
any
if we need to express an arbitrary type a protocol, for example to store multiple into an array, we can use
anywith
any, the underlying type can vary at runtimeany xxxis a single static type that has the capability to store any concreteX-conforming type dynamically, which allows us to use subtype polymorphism with value typesto allow for
anyrequired flexible storage, theany Xtype has a special representation in memoryyou can think of this representation as a fixed box:
if the concrete type can fit within the box, it will be stored there
the box will store a pointer to the instance otherwise
the static type
any Xthat can dynamically store any concreteXtype is called an existential typethe strategy of using the same representation for different concrete types is called type erasure
the concrete type is said to be erased at compile time, and the concrete type is only known at runtime
type erasure eliminates the type-level distinction between different
X-conforming instances, which allows us to use values with different dynamic types interchangeably as the same static type
some vs. any
the compiler can convert an instance of
any Xtosome xby unboxing the underlying value and passing it directly to thesome Xparameternew in Swift 5.7
some | any |
|---|---|
| holds a fixed concrete type | holds an arbitrary concrete type |
| guarantees type relationships | erases type relationships |
Guidelines:
use
someby defaultchange to
anywhen you need to store arbitrary values (arbitrary concrete types instances)

