Subtype vs parametric polymorphism
Essence
These are two ways to write code that works with "different types through one interface", and in Swift they have different mechanics and cost.
- Subtype polymorphism: a value of a subtype is used where the supertype is expected: a subclass instead of a class, a conformer instead of
any Protocol. The concrete implementation is chosen at runtime (vtable for classes, protocol witness table for protocols). - Parametric polymorphism: generics. A function or type is written once for an arbitrary
Twith constraints. The type is known at compile time and the compiler emits a specialized version.
Example
// Subtype polymorphism: a value of a subtype is used where the supertype
// is expected. Decided at runtime (vtable / witness table).
protocol Shape { func area() -> Double }
struct Circle: Shape { let r: Double; func area() -> Double { .pi * r * r } }
struct Square: Shape { let a: Double; func area() -> Double { a * a } }
let shapes: [any Shape] = [Circle(r: 1), Square(a: 2)]
let total = shapes.reduce(0) { $0 + $1.area() } // existential, dynamic
// Parametric polymorphism: code is written once for any T; the compiler
// specializes it per concrete type (static dispatch, no boxing).
func totalArea<S: Shape>(_ shapes: [S]) -> Double {
shapes.reduce(0) { $0 + $1.area() }
}
let circles = totalArea([Circle(r: 1), Circle(r: 2)]) // S == Circle
// `some Shape` = opaque type: parametric for the caller, one concrete type.
func makeShape() -> some Shape { Circle(r: 3) }
When to choose which
- Heterogeneous data (an array of different shapes, a list of screens): only subtyping works:
any Shape. - Homogeneous data and hot code: generics or
some: no boxing, inlining and specialization are possible. - A protocol with an
associatedtypecould not be used asany Pfor a long time; since Swift 5.7 it can (any Collection<Int>), but calls remain dynamic.
What the interviewer looks for
- Understanding that
any Pis a fixed-size container (3 words inline, otherwise a heap allocation) plus a pointer to the witness table. some Pas a return type: the caller does not know the concrete type but the compiler does; SwiftUI'ssome Viewis exactly this.- Class hierarchies are subtype polymorphism too, but in Swift they are needed less often: a protocol gives the same for value types.