다형성 함수 타입
다형성 함수 타입 (Polymorphic Function Types)
**다형성 함수 타입(polymorphic function type)**은 타입 파라미터를 받아들이는 함수 타입이에요. 예를 들어 볼게요:
// A polymorphic method:
def foo[A](xs: List[A]): List[A] = xs.reverse
// A polymorphic function value:
val bar: [A] => List[A] => List[A]
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// a polymorphic function type
= [A] => (xs: List[A]) => foo[A](xs)
본문
Scala에는 이미 다형성 메서드(polymorphic method), 즉 타입 파라미터를 받는 메서드가 있었어요. 위 foo 메서드가 그 예시이고, 타입 파라미터 A를 받죠. 지금까지는 이런 메서드를 위 bar 같은 다형성 함수 값으로 바꿀 수 없었어요. 다른 함수에 파라미터로 넘기거나 결과로 반환할 수 없었던 거죠.
Scala 3에서는 이제 이게 가능해졌어요. 위 bar 값의 타입은 이렇게 생겼죠:
[A] => List[A] => List[A]
이 타입은 "타입 A를 파라미터로 받고, 그다음 List[A] 타입의 리스트를 받아서 같은 타입 List[A]의 리스트를 돌려주는" 함수 값을 설명해요.
자세한 내용을 더 볼게요.
사용 예시 (Example Usage)
다형성 함수 타입은 특히 이런 상황에서 유용해요. 메서드의 호출자가 다형성이어야만 하는 함수를 제공해야 할 때죠. 즉 입력의 일부로 임의의 타입을 받아들여야 하는 함수를 넘겨야 할 때예요.
예를 들어, 간단한 언어(변수와 함수 적용만으로 이루어진)의 표현식을 강타입(strongly typed)으로 나타내는 데이터 타입이 있다고 해 볼게요:
enum Expr[A]:
case Var(name: String)
case Apply[A, B](fun: Expr[B => A], arg: Expr[B]) extends Expr[A]
이제 사용자들이 주어진 Expr의 모든 직접 하위 표현식(subexpression)에 함수를 매핑할 수 있는 방법을 제공하고 싶어요. 각 하위 표현식은 타입이 다를 수 있으므로, 주어지는 함수는 다형성이어야 해요. 다형성 함수 타입으로 이렇게 구현할 수 있어요:
enum Expr[A]:
case Var(name: String)
case Apply[A, B](fun: Expr[B => A], arg: Expr[B]) extends Expr[A]
import Expr.*
def mapSubexpressions[A](e: Expr[A])(f: [B] => Expr[B] => Expr[B]): Expr[A] =
e match
case Apply(fun, arg) => Apply(f(fun), f(arg))
case Var(n) => Var(n)
그리고 이 함수를 사용해서, 주어진 표현식의 각 하위 표현식을 어떤 wrap 함수 호출로 감싸 볼게요. wrap은 변수로 정의돼 있어요:
enum Expr[A]:
case Var(name: String)
case Apply[A, B](fun: Expr[B => A], arg: Expr[B]) extends Expr[A]
import Expr.*
def mapSubexpressions[A](e: Expr[A])(f: [B] => Expr[B] => Expr[B]): Expr[A] =
e match
case Apply(fun, arg) => Apply(f(fun), f(arg))
case Var(n) => Var(n)
val e0 = Apply(Var("f"), Var("a"))
val e1 = mapSubexpressions(e0)(
[B] => (se: Expr[B]) => Apply(Var[B => B]("wrap"), se))
println(e1) // Apply(Apply(Var(wrap),Var(f)),Apply(Var(wrap),Var(a)))
타입 람다와의 관계 (Relationship With Type Lambdas)
다형성 함수 타입을 **타입 람다(type lambda)**와 혼동하면 안 돼요. 전자(다형성 함수 타입)는 다형성 값의 타입을 설명하는 반면, 후자(타입 람다)는 타입 레벨에서의 실제 함수 값이에요.