Covariance, Contravariance, Invariance
What is Covariance, Contravariance and Invariance?
1. Covariance, Contravariance, Invariance π©βπ»
νλ‘κ·Έλλ° μΈμ΄μμ κ° νμ κ°μ μ°Έμ‘° κ΄κ³κ° μ°κ΄λ κ²λ€μ΄ λ§λ€. Classes μ μμ κ΄κ³, Generics μ μν κ΄κ³, Array μ κ°μ Monad 컨ν μ΄λ, ν¨μμ Parameters μ Return λ± μ¬λ¬ κ³³μμ μ΄λ¬ν κ΄κ³κ° μ‘΄μ¬νλ€. μλ μ§μ λ κ΄κ³λ³΄λ€ λ νμλκ±°λ λ νμλλ κ΄κ³ λλ μ΄λ¬ν κ΄κ³λ₯Ό κ°μ§ μλ λ 립μ μΈ κ΄κ³λ€μ΄ μ‘΄μ¬ν μ μλλ° μ΄κ²λ€μ λν μ μλ₯Ό λνλ΄λ κ²μ΄λ€.
μνμμ μ μ μ μμ°μ μ λ€μ΄μ΄κ·Έλ¨μ λ μ¬λ €λ³΄μ. μ μλ μμ°μλ₯Ό ν¬ν¨νκΈ° λλ¬Έμ μ μμ λ€μ΄μ΄κ·Έλ¨ μμ μμ°μκ° μνλ€. μ¬κΈ°μ μ μλ μμ νμ μ΄κ³ , μμ°μλ νμ νμ μ΄λ€. νλ‘κ·Έλλ°μ μμ΄μλ μ΄λ¬ν κ΄κ³κ° μλλ° κ°μ₯ λνμ μ΄κ³ μ½κ² μ ν μ μλ κ² λ°λ‘ Classes μ μμ κ΄κ³λ€.
λ¨μν νμ
μΌλ‘λ§ μκ°ν΄λ³΄μ. νμ
A
μ B
κ° μκ³ , I<U>
νκΈ°λ Type Argument U
λ₯Ό κ°λ Type Constructor
I
λ₯Ό λνλΈλ€.
- Covariance:
A β₯ B
μΌλ, λμΌνκ²I<A> β₯ I<B>
μ΄λ€. - Contravariance:
A β₯ B
μΌλ, λ°λλ‘I<A> β€ I<B>
μ΄λ€. - Bivariance: Covariance λ©΄μ λμμ Contravariance μ΄λ€. μ¦,
A β₯ B
μΌλ,I<A> = I<B>
μ΄λ€. - Variance: Covariance λλ Contravariance λλ Bivariance μ΄λ€.
- Invariance: Non-Variance μ΄λ€.
co-
: βν¨κ»βμ λ»μ λνλΈλ€. μ¦, Origin κ³Ό κ°μ λ°©ν₯μΌλ‘ νμ μ°Έμ‘° νΉμ±μ λνλΈλ€.
contra-
: βλ°λβμ λ»μ λνλΈλ€. μ¦, Origin κ³Ό λ°λ λ°©ν₯μΌλ‘ νμ μ°Έμ‘° νΉμ±μ λνλΈλ€.
μ΄ μ€ μ°λ¦¬κ° νμ μ ꡬλΆν λ μ΄μΌκΈ°νλ κ²μ Invariance, Covariance, Contravariance 3κ°μ§λ‘ μ’ λ νλ‘κ·Έλλ° κ΄μ μμ μ΄μΌκΈ°ν΄λ³΄μ.
Covariance
: μλ μ§μ λ κ²λ³΄λ€ λ νμλ νμ (more derived type)μ μ¬μ©ν μ μλ€.Contravariance
: μλ μ§μ λ κ²λ³΄λ€ λ νμλ νμ (less derived type)μ μ¬μ©ν μ μλ€.Invariance
: μλ μ§μ λ κ²λ§ μ¬μ©ν μ μλ€.
Covariant Type Parameters
λPolymorphism
κ³Ό λ§€μ° μ μ¬ν ν λΉμ μ¬μ©ν μ μλ€.
μΌλ°μ μΌλ‘ μ
λ ₯ μμΉμ ν΄λΉνλ Parameter Type μ Invariant
νκ±°λ Covariant
νκ³ , μΆλ ₯ μμΉμ ν΄λΉνλ
Return Type μ Covariant
νλ€. νμ§λ§ μ΄κ²μ κ° μΈμ΄λ§λ€ μλ‘ λ€λ₯Έ νΉμ§μ 보μ νκ³ μκΈ° λλ¬Έμ λ°λμ κ·Έλ¬ν κ²μ μλλ€.
μλ₯Ό λ€μ΄ Scala λ Parameter Type κ° Contravariant νκ³ , Eiffel μ Covariant νλ€.
Β | Type Relation | TypeScript 4.7 Syntax |
---|---|---|
Origin | A β₯ B | Β |
Covariance | I<A> β₯ I<B> |
<out T> |
Contravariance | I<A> β€ I<B> |
<in T> |
Invariance | I<A> , I<B> λ λ
립μ μ΄λ€. |
<in out T> |
Classes μ κΈ°λ³Έ λμμ λ νμλ νμ
(more derived type) Dog
λ₯Ό λ ν° μ»¨ν
μ΄λ Animal
μ λ£μ μ μλ€.
μ΄κ²μ subtyping
μ΄λΌ λΆλ₯Έλ€.
Dog
λAnimal
μ subtype μ΄κ³ νμ΄νλ₯Ό μ΄μ©ν΄ λ€μκ³Ό κ°μ΄ ννν μ μλ€.
Dog β Animal
μ΄κ²μ subtyping direction μ΄λΌ νλ€.
I<Dog> β I<Animal>
μ κ°μ΄ 컨ν μ΄λλ‘ λνν νμ μ΄ Origin νμ μ subtyping direction κ³Ό λμΌν λ°©ν₯μ κ°λ κ±ΈCovariance
, λ°λ λ°©ν₯μ κ°λ κ±ΈContravariance
λΌ νλ€.
2. Variance in TypeScript π©βπ»
1. Origin Types
class Animal {
private species: string;
constructor(species: string) {
this.species = species;
}
}
class Dog extends Animal {
private name: string;
constructor(species: string, name: string) {
super(species);
this.name = name;
}
}
const animal: Animal = new Dog('κ°μμ§', 'νΈλ')
interface Animal {
species: string
}
interface Dog extends Animal {
name: string
}
const animal: Animal = {
species: 'κ°μμ§',
name: 'νΈλ'
} as Dog
type Animal = {
species: string
}
type Dog = Animal & {
name: string
}
const animal: Animal = {
species: 'κ°μμ§',
name: 'νΈλ'
} as Dog
TypeScript λ JavaScript λ‘ λ체λμ΄μΌ νκ³ , μ¬μ€μ Classes κ° μ‘΄μ¬νμ§ μλλ€. νμ
μ μμ±νκ³ μμνλ λ°©λ²μ
class
, interface
, type
μΈμλ Constructor Function μ μ¬μ©νκ±°λ Object Literal,
Closures λ₯Ό μ¬μ©νλ λ± λ€μν λ°©λ²μ΄ μμΌλ μ λΆ ν¨μλ₯Ό μ΄μ©ν Object
κ°μ²΄λ₯Ό μμ±νλ€. λ°λΌμ μ΄λ€ λ°©λ²μ
μ¬μ©νλ Dog β Animal
μ subtyping direction μ κ°λλ€.
μ΄λ² μΉμ
μμλ κ°μ²΄λ₯Ό ννν λ κ°μ₯ μΌλ°μ μΌλ‘ μ¬μ©λλ interface
λ₯Ό μ¬μ©ν΄ νμ
μ νννλ€.
2. TypeScript 4.7
TypeScript λ 4.7 λ²μ λΆν° in
, out
modifier λ₯Ό μ§μνλ€. μλ κΈ°λ₯μ΄ μΆκ°λ κ²μ μλκ³ κΈ°μ‘΄μλ μ½λλ₯Ό μμ±νλ©΄
μ»΄νμΌλ¬κ° Covariance μΈμ§, Contravariance μΈμ§ μΆλ‘ νλ κ²μ μ¬μ©μκ° λͺ
μμ μΌλ‘ μμ±ν μ μλλ‘ modifier κ°
λμ
λ κ²μ΄λ€. μ΄ modifier κ° λμ
λ μ΄μ λ μ½λλ₯Ό 보λ κ² λ§μΌλ‘ μ½κ² μ νμ μ΄ν΄ν μ μλλ‘ μ¬μ©μμκ²λ λμμ μ£Όκ³ ,
μ»΄νμΌλ¬μ μλμ μ νμ±μ λμ΄λ κΈ°λ₯μ νκΈ° μν¨μ΄λ€.
type Getter<T> = () => T
type Setter<T> = (value: T) => void
λ₯Ό λͺ μμ μΌλ‘ νννλ©΄
type Getter<out T> = () => T
type Setter<in T> = (value: T) => void
μ΄ λλ€. λ§μ°¬κ°μ§λ‘
interface State<T> {
get: () => T
set: (value: T) => void
}
λ₯Ό λͺ μμ μΌλ‘ νννλ©΄
interface State<in out T> {
get: () => T
set: (value: T) => void
}
in
,out
modifier κ° μλ―Ένλ κ²μ GenericT
λ₯Ό μ΄λ»κ² μ¬μ©λλμ§λ₯Ό λͺ μνλ κ²μ΄λ€. λ°λΌμGetter
μtype Getter<in T> = () => T
λ‘ μμ±νλ©΄ compile-time error κ° λ°μλλ€. λ°λ©΄type Getter<in out T> = () => T
λ νΌλμ μ€ μλ μμΌλ μ»΄νμΌμ λ¬Έμ κ° λμ§λ μλλ€.μ΄μ¨λ
T
λ₯Ό input μΌλ‘ μ¬μ©ν λλin
modifier λ₯Ό, output μΌλ‘ μ¬μ©ν λλout
modifier λ₯Ό μ¬μ©ν΄μΌνλ€. κ·Έλ¦¬κ³ λλΆλΆμ TypeScript μμ€ν μμin
μ Contravariant νλ©°,out
μ Covariant ν νΉμ±μ κ°λλ€.κ·Έλ°λ°
in out
μΌλ‘ μμ±νλ©΄ μ΄λ»κ² λ κΉ? input κ³Ό output λͺ¨λT
λ₯Ό μ¬μ©νκ² λ€λ κ²μ΄λ―λ‘ Invariant νλ€.
3. Covariance
const dogArray: Array<Dog> = []
const animalArray: Array<Animal> = dogArray
Array 컨ν μ΄λλ‘ λνν Dog λ μ¬μ ν Array 컨ν μ΄λλ‘ λνν Animal μ subtype μ΄λ€.
[Dog] β [Animal]
Swift μ Array λ Origin Types μ subtyping direction κ³Ό λμΌν λ°©ν₯ κ°λ Covariance λ€.
Covariance μ λ λ€λ₯Έ μλ Function Retun Types
λ€.
const dogBuilder: () => Dog = () => ({} as Dog)
const animalBuilder: () => Animal = dogBuilder
Dog β Animal
κ³Ό λμΌν λ°©ν₯ () -> Dog
β () -> Animal
μ΄λ―λ‘ ν¨μμ Return Types λ Covariance
λ€.
4. Contravariance
κ·Έλ°λ° Function Parameter Types
μμλ Origin Types μ λ€λ₯Έ λ°©ν₯μ κ°λλ€.
const dogHandler: (_: Dog) => void = (dog) => console.log(dog)
const animalHandler: (_: Animal) => void = dogHandler // error
Dog β Animal
μ΄μ§λ§ (Dog) -> void
β (Animal) -> void
λ νμ©λμ§ μλλ€. μ΄κ²μ Type Unsafe νκΈ°
λλ¬Έμ΄λ€.
const animal: Animal = {} as Dog
const animalArray: Animal[] = [{} as Dog, {} as Dog, {} as Dog]
Covariant ν μ μ½λλ₯Ό 보μ. μ΄κ²μ΄ νμ©λλ κ²μ Animal μ΄λΌλ 컨ν μ΄λκ° λ ν¬κΈ° λλ¬Έμ Dog λ₯Ό μ μ₯νλ κ²μ΄ κ°λ₯νκΈ° λλ¬Έμ΄λ€.
const dogBuilder: () => Dog = () => ({} as Dog)
const animalBuilder: () => Animal = dogBuilder
λ§μ°¬κ°μ§λ‘, Function Return Types
λ μ΄λκ°μ μ μ₯νκΈ° μν¨μ΄λΌλ κ²μ κΈ°μ΅νμ.
μ μ΄μ const animal: Animal = {} as Dog
μ½λλ Object μμ±μ Class
Syntax λ₯Ό μ¬μ©ν΄ μμ±νμ κ²½μ°,
constructor
λ₯Ό νΈμΆνλ μ½λμ΄κ³ , μ΄κ²μ Class Instances λ₯Ό μμ±ν΄ λ°ννλ νΉμν ν¨μμ΄λ€.
μλ₯Ό λ€μ΄ Classes λ₯Ό Singleton
μΌλ‘ λ§λ€μ΄ Instances λ₯Ό μμ±νλ λ€λ₯Έ ν¨μλ₯Ό λ§λ€μ΄ μ¬μ©νλ€κ³ μ΄ κ·μΉμ΄ κΉ¨μ§λ€λ κ²μ΄
λ§μ΄ μ λλ κ²μ΄λ€. μ¦, Function Return Types λ μ΄λκ° μ μ₯λκΈ° μν¨μ΄κ³ , λΉμ°ν λ ν° μ»¨ν
μ΄λλ‘ λ΄λ³΄λ΄λ κ²μ΄
κ°λ₯ν΄μΌ νλ€.
νμ§λ§ Function Parameter Types
μ κ²½μ°λ λ€λ₯΄λ€! μ΄κ²μ μ΄λκ°μ μ μ₯λκΈ° μν¨μ΄ μλ,
Functions μ Body λ΄μμ μ΄λ ν λΉμ¦λμ€ λ‘μ§μ μννκΈ° μν¨μ΄λ€. μ¦, Dog μλ§ μ‘΄μ¬νλ κ²λ€μ νμλ‘
νλ€λ κ²μΈλ°, λ§μ½ Animal μ΄ νμ©λμ΄ λ λ€λ₯Έ νμ νμ
μΈ Cat μ΄ λ€μ΄μ¬ κ²½μ°, λΉμ¦λμ€ λ‘μ§μ μνν μ μκ² λκ³ ,
μλ¬λ₯Ό λ°μμν¨λ€.
const animalHandler: (_: Animal) => void = (animal) => console.log(animal)
const dogHandler: (_: Dog) => void = (dog) => animalHandler
λ°λΌμ μμ νμ
μΈ Animal μ΄ νμ νμ
μΈ Dog λλ Cat μΌλ‘ subtyping direction μ΄
μμ λλ κ²μ Type Safe νμ§λ§, μ λ°©ν₯μ Type Unsafe ν λ°λμ μν©μ΄ λ§λ€μ΄μ§λ κ²μ΄λ€.
λ°λ‘ μ μ₯μ΄ λκΈ° μν¨μΈκ°?
, λΉμ¦λμ€ λ‘μ§μ μννκΈ° μν¨μΈκ°
μ μ°¨μ΄μ λ°λΌ μ΄λ€ κ²μ΄ Type Safe νκ°λ₯Ό μκ°ν΄λ³΄λ©΄
μ μ μλ κ²μ΄λ€.
λ°λΌμ Function Parameter Types λ Dog β Animal
μΈλ° (Animal) -> void
β (Dog) -> void
κ° μ±λ¦½λλ―λ‘
Origin Types μ subtyping direction μ μλ°©ν₯μ κ°λ Contravariance
λ€.
Function μ Parameter Types λ
Contravariant
νκ³ , Return Types λCovariant
ν΄μΌ Type Safe νλ€.
μ’ λ 볡μ‘ν μΌμ΄μ€λ₯Ό μκ°ν΄λ³΄μ.
// Case 1
const animalResolverLater: ((f: (animal: Animal) => void) => void) = (f) => f({} as Animal)
const dogResolverLater: ((f: (dog: Dog) => void) => void) = animalResolverLater // error
λ λΆκ°λ₯νλ°
// Case 2
const dogResolverLater: ((f: (dog: Dog) => void) => void) = (f) => f({} as Dog)
const animalResolverLater: ((f: (animal: Animal) => void) => void) = dogResolverLater
λ κ°λ₯νλ€.
μΌν 보면 Case 1 μ μ½λκ° Parameter Types κ° Animal
μμ Dog
λ‘ μμ λλ λ§λ κ² κ°μ 보μΈλ€. νμ§λ§
μμλλ‘ μ°¨κ·Όμ°¨κ·Ό λ°μ Έλ³΄λ©΄ Case 2 μ μ½λκ° κ°λ₯ν μΌμ΄μ€λΌλ κ²μ μ μ μλ€.
const dog: Dog = {} as Dog
const animal: Animal = dog
Animal
μ Dog
λ₯Ό μ μ₯νλ€. Covariant ν μ μ μ½λλ€.
const animalHandler: (_: Animal) => void = (animal) => console.log(animal)
const dogHandler: (_: Dog) => void = (dog) => animalHandler
Dog
μ Animal
Parameter Types κ° μ μ₯λλ―λ‘ μμ μ΄ λλ€. Contravariant ν μ μ μ½λλ€.
const dogResolverLater: ((f: (dog: Dog) => void) => void) = (f) => f({} as Dog)
const animalResolverLater: ((f: (animal: Animal) => void) => void) = dogResolverLater
μ΄μ λ€μ μμ λμ΄ (Animal) -> void
μ (Dog) -> void
Parameter Types κ° μ μ₯λλ―λ‘ μμ μ΄ λλ€.
Contravariant ν μ μμ μΈ μ½λλ€. λ§μ½, μ΄ μ½λκ° μ μ΄ν΄κ° λμ§ μλλ€λ©΄ alias λ₯Ό μ¬μ©ν΄ κ°λ
μ±μ λμ¬λ³΄μ.
const dog: Dog = {} as Dog
const animal: Animal = dog
type AnimalHandler = (animal: Animal) => void
type DogHandler = (dog: Dog) => void
const animalHandler: AnimalHandler = (animal) => console.log(animal)
const dogHandler: DogHandler = (dog) => animalHandler
const dogResolverLater: (f: DogHandler) => void = (f) => f({} as Dog)
const animalResolverLater: (f: AnimalHandler) => void = dogResolverLater
μ¦, Animal
κ³Ό Dog
λ₯Ό 보λ κ²μ΄ μλλΌ (Animal) -> void
μ (Dog) -> void
λ₯Ό λ΄μΌ νλ κ²μ΄λ€.
5. Invariance
Invariant λ₯Ό λ€λ£°λ μ μν΄μΌ ν κ²μ΄ λ°λ‘ Generics
μ΄λ€. μ TypeScript 4.7 μμ
μκ°ν in
, out
modifier μ ν¨κ» μμ보μ.
class Container {
item: Animal
constructor(item: Animal) {
this.item = item
}
}
const containerStoreDog: Container = new Container({} as Dog)
const containerStoreAnimal: Container = containerStoreDog
κΈ°λ³Έμ μΌλ‘ Covariant νκΈ° λλ¬Έμ μ μ½λλ μ μμ΄λ€.
νμ§λ§ Generics λ₯Ό μ¬μ©ν μ½λμμλ μ΄λ¨κΉ?
class Container<T> {
item: T
constructor(item: T) {
this.item = item
}
}
const animalContainer: Container<Animal> = new Container<Animal>({} as Animal)
const dogContainer: Container<Dog> = animalContainer // error
const dogContainer: Container<Dog> = new Container<Dog>({} as Dog)
const animalContainer: Container<Animal> = dogContainer
TypeScript λ κΈ°λ³Έμ μΌλ‘ Genericsλ₯Ό μ¬μ©ν΄ 컨ν μ΄λλ₯Ό μμ±νλλΌλ Covariant λ₯Ό μ μ§νλ€!
Generics λ₯Ό μ¬μ©ν κ²½μ° Invariant
νλλ‘ νκΈ° μν΄ in out
modifier λ₯Ό μ¬μ©ν΄λ³΄μ.
class Container<in out T> {
item: T
constructor(item: T) {
this.item = item
}
}
const animalContainer: Container<Animal> = new Container<Animal>({} as Animal)
const dogContainer: Container<Dog> = animalContainer // error
const dogContainer: Container<Dog> = new Container<Dog>({} as Dog)
const animalContainer: Container<Animal> = dogContainer // error
μ΄μ Container
λ input κ³Ό output λͺ¨λ T
λ₯Ό μ¬μ©νκ² λ€λ κ²μ΄λ―λ‘ Invariant νλ€.
3. Variance in Swift π©βπ»
1. Origin Types
class Animal {}
class Dog: Animal {}
let animal: Animal = Dog()
Dog β Animal
μ subtyping direction μ κ°λλ€.
2. Covariance
let dogArray: [Dog] = []
let animalArray: [Animal] = dogArray
Array 컨ν μ΄λλ‘ λνν Dog λ μ¬μ ν Array 컨ν μ΄λλ‘ λνν Animal μ subtype μ΄λ€.
[Dog] β [Animal]
Swift μ Array λ Origin Types μ subtyping direction κ³Ό λμΌν λ°©ν₯ κ°λ Covariance
λ€.
Covariance μ λ λ€λ₯Έ μλ Closures Retun Types
λ€.
let dogBuilder: () -> Dog = { Dog() }
let animalBuilder: () -> Animal = dogBuilder
Dog β Animal
κ³Ό λμΌν λ°©ν₯ () -> Dog
β () -> Animal
μ΄λ―λ‘ ν¨μμ Return Types λ Covariance
λ€.
3. Contravariance
κ·Έλ°λ° Closures Parameter Types
μμλ Origin Types μ λ€λ₯Έ λ°©ν₯μ κ°λλ€.
let dogHandler: (Dog) -> Void = { print($0) }
let animalHandler: (Animal) -> Void = dogHandler // error
Dog β Animal
μ΄μ§λ§ (Dog) -> Void
β (Animal) -> Void
λ νμ©λμ§ μλλ€. μ΄κ²μ Type Unsafe νκΈ°
λλ¬Έμ΄λ€.
let animal: Animal = Dog()
let animalArray: [Animal] = [Dog(), Dog(), Dog()]
Covariant ν μ μ½λλ₯Ό 보μ. μ΄κ²μ΄ νμ©λλ κ²μ Animal μ΄λΌλ 컨ν μ΄λκ° λ ν¬κΈ° λλ¬Έμ Dog λ₯Ό μ μ₯νλ κ²μ΄ κ°λ₯νκΈ° λλ¬Έμ΄λ€.
let dogBuilder: () -> Dog = { Dog() }
let animalBuilder: () -> Animal = dogBuilder
λ§μ°¬κ°μ§λ‘, Closure Return Types
λ μ΄λκ°μ μ μ₯νκΈ° μν¨μ΄λΌλ κ²μ κΈ°μ΅νμ. μ μ΄μ let animal: Animal = Dog()
μ½λλ Classes μ initializers
λ₯Ό νΈμΆνλ μ½λμ΄κ³ , μ΄κ²μ Class Instances λ₯Ό μμ±ν΄ λ°ννλ νΉμν ν¨μμ΄λ€.
μλ₯Ό λ€μ΄ Classes λ₯Ό Singleton
μΌλ‘ λ§λ€μ΄ Instances λ₯Ό μμ±νλ λ€λ₯Έ ν¨μλ₯Ό λ§λ€μ΄ μ¬μ©νλ€κ³ μ΄ κ·μΉμ΄ κΉ¨μ§λ€λ κ²μ΄
λ§μ΄ μ λλ κ²μ΄λ€. μ¦, Closure Return Types λ μ΄λκ° μ μ₯λκΈ° μν¨μ΄κ³ , λΉμ°ν λ ν° μ»¨ν
μ΄λλ‘ λ΄λ³΄λ΄λ κ²μ΄
κ°λ₯ν΄μΌ νλ€.
νμ§λ§ Closure Parameter Types
μ κ²½μ°λ λ€λ₯΄λ€! μ΄κ²μ μ΄λκ°μ μ μ₯λκΈ° μν¨μ΄ μλ,
Closures μ Body λ΄μμ μ΄λ ν λΉμ¦λμ€ λ‘μ§μ μννκΈ° μν¨μ΄λ€. μ¦, Dog μλ§ μ‘΄μ¬νλ κ²λ€μ νμλ‘
νλ€λ κ²μΈλ°, λ§μ½ Animal μ΄ νμ©λμ΄ λ λ€λ₯Έ νμ νμ
μΈ Cat μ΄ λ€μ΄μ¬ κ²½μ°, λΉμ¦λμ€ λ‘μ§μ μνν μ μκ² λκ³ ,
μλ¬λ₯Ό λ°μμν¨λ€.
let animalHandler: (Animal) -> Void = { print($0) }
let dogHandler: (Dog) -> Void = animalHandler
λ°λΌμ μμ νμ
μΈ Animal μ΄ νμ νμ
μΈ Dog λλ Cat μΌλ‘ subtyping direction μ΄
μμ λλ κ²μ Type Safe νμ§λ§, μ λ°©ν₯μ Type Unsafe ν λ°λμ μν©μ΄ λ§λ€μ΄μ§λ κ²μ΄λ€.
λ°λ‘ μ μ₯μ΄ λκΈ° μν¨μΈκ°?
, λΉμ¦λμ€ λ‘μ§μ μννκΈ° μν¨μΈκ°
μ μ°¨μ΄μ λ°λΌ μ΄λ€ κ²μ΄ Type Safe νκ°λ₯Ό μκ°ν΄λ³΄λ©΄
μ μ μλ κ²μ΄λ€.
λ°λΌμ Closure Parameter Types λ Dog β Animal
μΈλ° (Animal) -> Void
β (Dog) -> Void
κ° μ±λ¦½λλ―λ‘
Origin Types μ subtyping direction μ μλ°©ν₯μ κ°λ Contravariance
λ€.
Closure μ Parameter Types λ
Contravariant
νκ³ , Return Types λCovariant
ν΄μΌ Type Safe νλ€.
μ’ λ 볡μ‘ν μΌμ΄μ€λ₯Ό μκ°ν΄λ³΄μ.
// Case 1
let animalResolverLater: ((Animal) -> Void) -> Void = { f in f(Animal()) }
let dogResolverLater: ((Dog) -> Void) -> Void = animalResolverLater // error
λ λΆκ°λ₯νλ°
// Case 2
let dogResolverLater: ((Dog) -> Void) -> Void = { f in f(Dog()) }
let animalResolverLater: ((Animal) -> Void) -> Void = dogResolverLater
λ κ°λ₯νλ€.
μΌν 보면 Case 1 μ μ½λκ° Parameter Types κ° Animal
μμ Dog
λ‘ μμ λλ λ§λ κ² κ°μ 보μΈλ€. νμ§λ§
μμλλ‘ μ°¨κ·Όμ°¨κ·Ό λ°μ Έλ³΄λ©΄ Case 2 μ μ½λκ° κ°λ₯ν μΌμ΄μ€λΌλ κ²μ μ μ μλ€.
let dog: Dog = Dog()
let animal: Animal = dog
Animal
μ Dog
λ₯Ό μ μ₯νλ€. Covariant ν μ μ μ½λλ€.
let animalHandler: (Animal) -> Void = { print($0) }
let dogHandler: (Dog) -> Void = animalHandler
Dog
μ Animal
Parameter Types κ° μ μ₯λλ―λ‘ μμ μ΄ λλ€. Contravariant ν μ μ μ½λλ€.
let dogResolverLater: ((Dog) -> Void) -> Void = { f in f(Dog()) }
let animalResolverLater: ((Animal) -> Void) -> Void = dogResolverLater
μ΄μ λ€μ μμ λμ΄ (Animal) -> Void
μ (Dog) -> Void
Parameter Types κ° μ μ₯λλ―λ‘ μμ μ΄ λλ€.
Contravariant ν μ μμ μΈ μ½λλ€. λ§μ½, μ΄ μ½λκ° μ μ΄ν΄κ° λμ§ μλλ€λ©΄ typealias λ₯Ό μ¬μ©ν΄ κ°λ
μ±μ λμ¬λ³΄μ.
let dog: Dog = Dog()
let animal: Animal = dog
typealias AnimalHandler = (Animal) -> Void
typealias DogHandler = (Dog) -> Void
let animalHandler: AnimalHandler = { print($0) }
let dogHandler: DogHandler = animalHandler
let dogResolverLater: (DogHandler) -> Void = { f in f(Dog()) }
let animalResolverLater: (AnimalHandler) -> Void = dogResolverLater
μ¦, Animal
κ³Ό Dog
λ₯Ό 보λ κ²μ΄ μλλΌ (Animal) -> Void
μ (Dog) -> Void
λ₯Ό λ΄μΌ νλ κ²μ΄λ€.
4. Invariance
Swift μ μμ€ν
μ λλΆλΆ Invariant νλ€. κ·Έ μ€ μ μν΄μΌ ν κ²μ΄ λ°λ‘ Generics
μ΄λ€.
struct Container {
var item: Animal
}
let containerStoreDog: Container = Container(item: Dog())
let containerStoreAnimal: Container = containerStoreDog
κΈ°λ³Έμ μΌλ‘ Structures μ Classes λ Covariant νλ€. Structures λμ Structures λ₯Ό μ¬μ©ν΄λ λΉμ°ν μ μ½λλ μ μμ΄λ€.
νμ§λ§ Generics λ₯Ό μ¬μ©ν μ½λμμλ μ΄λ¨κΉ?
struct Container<T> {
var item: T
}
let animalContainer: Container<Animal> = Container(item: Animal())
let dogContainer: Container<Dog> = animalContainer // error
let dogContainer: Container<Dog> = Container(item: Dog())
let animalContainer: Container<Animal> = dogContainer // error
Generics
λ νμ
μΆλ‘ μ μ¬μ©ν λμ νμ
μΌλ‘ μλ§μ μ½λμ μ€λ²λ‘λ©μ νλμ μ½λλ‘ μ€μ΄λ©° Type Safe ν μ½λλ₯Ό
λ§λ€μ΄μ€λ€. νμ§λ§ Generics λ Invariant
νκΈ° λλ¬Έμ μ£Όμν΄μΌνλ€.
Reference
- βCovariance and contravariance in generics.β Microsoft.Net. Sep. 15, 2021, Covariance and contravariance in generics.
- delmaSong. βitem 32. μ λ€λ¦κ³Ό κ°λ³μΈμ(varargs)λ₯Ό ν¨κ» μΈ λλ μ μ€νλΌβ GitHub. Dec. 28, 2020, item 32. μ λ€λ¦κ³Ό κ°λ³μΈμ(varargs)λ₯Ό ν¨κ» μΈ λλ μ μ€νλΌ.
- Daniel Rosenwasser. βAnnouncing TypeScript 4.7β. Microsoft TypeScript. May. 24, 2022, Announcing TypeScript 4.7 - Optional Variance Annotations for Type Parameters.
- βCovariance and contravariance (computer science).β Wikipedia. Dec. 12, 2023, Wikipedia - Inheritance in object-oriented languages.
- aunnn. βCovariance and Contravariance in Swift.β Medium, Feb. 24, 2018, Covariance and Contravariance in Swift.