본인쟝은 자바로 헬로월드도 찍을줄 모르니까 한귀로 듣고 한귀로 흘려도 댐

1. 상속 = 데이터 + 다형성 + 코드 재사용
상속이 데이터와 동작(메서드)를 함께 다룬다는 말은 흔히 있는데, 좀 더 세분화가 가능하다고 생각함. (위키피디아에서도 언급되는것 같음)

내가 볼 때 상속은 적어도
- 데이터
- Subtyping polymorphism (업캐스팅)
- Ad hoc polymorphism (인터페이스. 오버라이딩이 되면 클래스 자체가 인터페이스로서의 기능도 한다고 생각함)
- 코드 재사용

이렇게 4개가 섞여있음.
문제는 이 4가지가 한번에 일어나기 때문에 한가지씩 개별적으로 제어하기가 힘듬. 인터페이스가 더 나은 해결책으로 제시되는것도 이 4가지에서 Ad hoc polymorphism만을 분리했기 때문이라고 생각함.

2. 확장을 닫는 방법이 없음
Subtyping 관계 S <: T 는 forall x. (x : S) -> (x : T) 가 만족됨을 이야기함.
Liskov식으로 이야기 하면
forall φ. (forall x:T. φ(x)) -> (forall x:S. φ(x))
집합으로 보면 S가 T의 subset이라고 생각할 수 있는거고.

그런데 프로그래밍 언어에서 쓰이는 상속 관계를 보면 미묘하게 느낌이 다름.
A : extends B 를 봅시다.
이건 기존에 정의된 B의 부분집합인 A를 정의한다는 말이 아니라, 기존의 B를 A까지 포함하는 새로운 집합으로 확장한다에 가까움.
그러니까 상속이 일어날때마다 모든 superclass들의 범주가 늘어나고, 상속을 완전히 막지 않는 이상 확장을 제어할 수 없음.

이 점에 있어서 클래스는 데이터보다도 인터페이스(타입클래스)와 유사함. 하스켈의 경우를 보면, 데이터는 닫혀있고 타입클래스는 열려있음. 즉, 데이터 타입의 element가 될 수 있는것들은 데이터를 정의하는 순간 결정되고, 타입클래스의 instance는 언제든 추가할 수 있음.

문제는 객체지향의 클래스는 데이터를 다루는데도 열려있다는 점임. 이건 꽤나 심각한 문제라고 생각함.
예를 들어서 집합이 닫혀있으면 case analysis를 통해서 어떤 함수가 항상 옳바르게 작동함을 검증할 수 있음.
OOP 클래스에서는 상속이 일어날때마다 집합이 늘어나기 때문에 비슷한 검증을 하려면 모든 자식클래스들에 대해 전부 검증해야하고, 그마저도 언제든지 새로운 자식클래스를 만들 수 있기 때문에 결국 검증책임을 자식클래스 쪽에 부과하는 수밖에 없음. 이건 결국 superclass가 interface와 비슷한 역할을 한다는 말임.

그런데 이건 클래스를 정보 은닉, 모듈화에 사용하는거랑은 정반대의 개념임. 흔히 생각하는 private를 사용하는 정보은닉의 주체는 superclass인데, interface에서 정보은닉의 주체는 각각의 instance들이니까.

그렇지 않으면 부모 자식 클래스간에 서로의 정보를 은닉한다는 소린데, 이게 OOP에서 지향하는 바인지는(혹시 그럼?) 잘 모르겠지만 적어도 하스켈에서 의존성의 방향은 항상 일방적임.

내 결론은 :
정보 은닉과 모듈화의 대상은 데이터와 메서드임. 그런데 클래스가 인터페이스와 비슷한 역할을 하면 이에 모순됨. 그러니 코드 재사용, 데이터 구조, 다형성을 전부 분리해야함.
그리고 바람직한 Subtyping의 형태는, 확장이 아니라 구체화라고 생각함.
subtyping이 일어날 때 범주가 넓어지는게 아니라, 기존의 클래스의 일부분으로 제한된 새로운 범주를 만드는 식으로. 물론 이건 subtyping이라는 것만 빼면 상속이랑은 전혀 다르다고 봄.

- dc official App