종류 다형성

종류 다형성 (Kind Polymorphism)

Scala의 타입 시스템은 타입 파라미터가 어떤 kind(종류)를 가져야 하는지 엄격하게 지켜요. 그런데 때로는 어떤 kind든 받아들이는 타입 파라미터가 있으면 편리한 경우가 있어요. Scala 3의 AnyKind를 쓰면 바로 그런 '종류 다형성'을 구현할 수 있습니다.

출처: Scala 3 Reference

본문

보통 Scala의 타입 파라미터는 kind로 나뉘어요. 1급(first-level) 타입은 값의 타입이고, 고차(higher-kinded) 타입은 ListMap 같은 타입 생성자예요. 타입의 kind는 그것이 부분타입인 최상위 타입으로 나타나요. 일반 타입은 Any의 부분타입이고, 공변 단일 인자 타입 생성자(예: List)는 [+X] =>> Any의 부분타입이며, Map 타입 생성자는 [X, +Y] =>> Any의 부분타입이에요.

타입은 자기 kind가 허용하는 방식으로만 사용할 수 있어요. Any의 부분타입은 타입 인자에 적용될 수 없는 반면, [X] =>> Any의 부분타입은 같은 kind의 타입 파라미터에 전달되지 않는 한 반드시 타입 인자에 적용되어야 해요.

때로는 하나 이상의 kind를 가질 수 있는 타입 파라미터가 있으면 좋을 때가 있어요. 예를 들어 어떤 kind의 파라미터에 대해서도 동작하는 암시적 값을 정의하고 싶은 경우죠. 이제 이것이 (부분타입) 종류 다형성의 한 형태로 가능해졌어요. 종류 다형성은 타입의 상한으로 쓸 수 있는 특별한 타입 scala.AnyKind에 의존합니다.

def f[T <: AnyKind] = ...

그러면 f의 실제 타입 인자는 임의의 kind를 가진 타입이 될 수 있어요. 그래서 다음은 모두 법적으로 유효합니다:

f[Int]
f[List]
f[Map]
f[[X] =>> String]

AnyKind 상한을 가진 타입 파라미터와 추상 타입을 우리는 any-kinded 타입이라고 불러요. any-kinded 타입의 실제 kind는 알 수 없기 때문에, 그 사용은 크게 제한되어야 해요. any-kinded 타입은 값의 타입이 될 수도, 타입 파라미터로 인스턴스화될 수도 없어요. 그래서 any-kinded 타입으로 할 수 있는 일은 거의 다른 any-kinded 타입 인자에 전달하는 것뿐이에요. 그럼에도 이것은 주로 implicit의 고급 활용을 통해, kind를 넘나드는 흥미로운 일반화를 이끌어내기에 충분합니다.

(todo: insert good concise example)

몇 가지 기술적 세부 사항을 말하면: AnyKindAny처럼 합성된 클래스이지만 멤버는 하나도 없어요. 다른 클래스를 확장하지도 않습니다. abstractfinal로 선언되어 있어서 인스턴스화할 수도, 확장할 수도 없어요.

AnyKind는 Scala의 부분타입 시스템에서 특별한 역할을 해요. kind가 무엇이든 모든 다른 타입의 상위 타입이죠. 또한 모든 다른 타입과 kind 호환되는 것으로 간주됩니다. 게다가 AnyKind는 고차 타입으로 취급되어(그래서 값의 타입으로 쓸 수 없어요) 동시에 타입 파라미터도 없어서(그래서 인스턴스화할 수도 없어요) 인스턴스화될 수 없어요.

참고: 이 기능은 이제 안정화되었어요. 컴파일러 플래그 -Yno-kind-polymorphism은 3.7.0부터 deprecated이며, 효과가 없고(무시되고), 미래 버전에서 제거될 예정입니다.