Abstraction
<T> where T: Idea
- Model with concrete types
- identify common capabilities
- build interface
- write generic code
Polymorphism
allows one piece of code to have many behaviors depending on how the code is used.
- Overloads achieve
ad-hoc polymorphism
- Subtypes achieve
subtype polymorphism
- Generics achieve
parametric polymorphism
A
protocol
separate the ideas about what a type does from the implementation details.
- can use protocols with struct, enums, actors, etc.
func feed<A: Animal>(_ animal: A)
func feed<A>(_ animal: A) where A: Animal
func feed(_ animal: some Animal)
Opaque
- underlying type for
some
// local vaariable
// must have initial value
let animal: some Animal = Horse()
// CANNOT change type later
// X
var animal: some Animal = Horse()
animal = Chicken()
// parameters
func feed(_ animal: some Animal)
feed(Horse())
feed(Chicken())
// Results
// should have same return type
func makeView(for farm: Farm) -> some View {
// WRONG, should return same type
if condition {
return FarmView(farm)
} else {
return EmptyView()
}
}
// can fix above issue by ViewBuilder DSL
// to transform control-flow statements to
// have same underlying return type for each branch
@ViewBuilder
func makeView(for farm: Farm) -> some View {
// WRONG, should return same type
if condition {
return FarmView(farm)
} else {
return EmptyView()
}
}
- one associatedType require another associateType
protocol Animal {
associatedType Feed: AnimalFeed
associatedType Habitat
func eat(_ food: Feed)
}
struct Farm {
func feed(_ animal: some Animal) { ... }
func buildHome<A>(for animal: A) -> A.Habitat where A: Animal { ... }
}
- use type parameter in generic type
- opaque name must always be named for generic types
struct Silo<Material> {
private var storage: [Material]
init(storing materials: [Material]) {
self.storage = materials
}
}
var hayStorage: Silo<Hay>
Arbitrary type
any
express arbitrary type- sometimes, value small enough to fit inside box, if other values are too large, value will be allocated elsewhere, box stores a pointer to the value
existential type
: static type can dynamically store any concrete typetype erasure
: use same representation for different concrete type- concrete type is said to be erased at compile time, the concrete type is only known at runtime
- new in Swift5.7,
- use
any
keyword for protocols with associated types - compiler can convert instance of
any
tosome
by unboxing underlying value and passing it directly tosome
parameter- unboxing as the compiler open the box and take out value stored inside
- use
Capabilities of some
and any
some
- holds fixed concrete type
- guarantee type relationships
any
- holds arbitrary concrete type
- erases type relationships\
- write
some
by default, changesome
toany
when need to store arbitrary values
Example
protocol Animal {
associatedType Feed: AnimalFeed
func eat(_ food: Feed)
}
struct Farm {
func feed(_ animal: some Animal) {
let crop = type(of: animal).Feed.grow()
let produce = crop.harvest()
animal.eat(produce)
}
func feedAll(_ animals: [any Animal]) {
for animal in animals {
// can convert `any` to `some`
feed(animal)
}
}
}
Comments
Join the discussion for this article at here . Our comments is using Github Issues. All of posted comments will display at this page instantly.