Variance(공변성)은 parametric한 타입이 가질 수 있는 특성임. FP하고 OOP에서 variance의 정의가 미묘하게 다르긴 하지만 결국 같은거라고 볼 수 있음.

우선 OOP부터 보자. OOP에서 variance는 subtyping을 가지고 정의됨.

리스트 타입 List<A> 를 예로 들어보자.
Cat <: Animal, Dog <: Animal 이라고 할 때 (<: 는 subtyping relation 기호임. Cat이 Animal의 서브클래스라는 의미.)
List<Cat>은 List<Animal>로 간주할 수 있음. Cat을 읽어서 Animal로 쓰는건 아무 문제가 없지.
반면 List<Animal>을 List<Cat>으로 간주할 수는 없음. 왜냐면 List<Animal>에 Dog이 들어 있을 수도 있으니까.

이걸 일반화 시켜보면 List에 대해서 다음과 같은 타입 추론 규칙을 세우는게 합리적이란걸 알 수 있음.
if A <: B then List<A> <: List<B>
이런 규칙이 성립하는 List와 같은 타입을 covariant(공변) 하다고 말함.

(사실 코딩조무사님이 말했듯이 가변을 허용하면 공변성이 깨짐. 그경우에는 invariant로 취급해야 type safe함. insert 메서드의 타입을 고려하면 당연하다고도 볼 수 있음)

그럼 모든 제너릭 타입은 covariant한가? 하면 그건 아님. predicate라고 부르는 타입을 한번 보자. (논리학 용어 predicate에서 나온 말임)
Pred<A> = A -> Bool
(OOP스러운 문법을 사용하지 못한건 양해 부탁. 어떻게 해야할지 모르겠음)
p : Pred<A> 는 어떤 x:A 를 인자로 받아서 x가 특정 조건을 만족하는지 여부를 Bool값으로 리턴하는 함수임.
아까 Cat/Animal을 다시 가져와서, Pred<Cat>과 Pred<Animal>은 어떤 관계가 있을까?
아까랑은 다르게 Pred<Cat>을 Pred<Animal>로 간주하면 문제가 생김. Pred<Cat>은 Cat에 대해서만 올바른 작동을 보장하는데, Pred<Animal>은 Dog도 인자로 넘길 수 있으니까.
오히려 Pred<Animal>을 Pred<Cat>으로 보는건 문제가 없어짐.

그래서 Pred에 대해서는
if A <: B then Pred<B> <: Pred<A>
가 성립함. 이렇게 subtyping 관계가 반대인 타입들을 contravariant(반변) 이라고 부름.

그럼 어떤 제너릭 타입이 covariant 인지 contravariant 아니면 인지를 어떻게 아나?
이건 굉장하 간단한 규칙으로 알 수 있음. 사실 프로그래밍 언어에서 모든 contravariant는 함수 타입으로부터 기인함.

함수 타입 A -> B는 A, B 두개의 타입 인자에 대한 제너릭 타입으로 볼 수 있는데, A에 대해서는 contravariant하고 B에 대해서는 covariant함.

그리고 또 신기한건 contravariant가 두번 겹치면 covariant해짐. 예를들어 Pred<Pred<A>> 같은 타입은 A에 대해 covariant함.
그래서 covariant를 positive, contravariant를 negative라고도 부르고, A -> B에서 A를 negative position, B를 positive position에 있다고 말함.

어떤 타입이 covariant인지 contravariant인지 알고 싶으면 그냥 타입이 negative에 있는지 positive에 있는지 보고, negative가 홀수번이면 negative, 짝수번이면 positive라고 하면 됨.

그럼 invariant(불변)은 뭐냐,
이건 type parameter가 negative랑 positive에 동시에 위치할 때 발생하는데
Foo<A> = (List<A>, Pred<A>)
처럼 positive인 List와 negative인 Pred의 튜플로 이루어진 Foo는 covariant규칙도, contravariant규칙도 적용할 수 없음. 그래서 이런 타입을 invariant하다고 부름.

한가지 더 bivariant도 있는데, covariant하면서 동시에 contravariant한거임. 이건 FP에서 이야기 하기로.

아까 List가 가변일때 invariant하다는 이야기를 좀 더 해보자면, 정확히는 가변이면서 인자를 받는게 문제라고 볼 수 있음.
리스트 삽입은 x:A를 받아서 List<A>를 수정하는데 A를 인자로 받는다는건 A에 대해 negative해지는거고 결국 covariance가 깨짐. 리스트에서 임의의 원소를 삭제하는 연산은 mutation을 사용하지만 공변성을 깨지 않음.

- dc official App