다형 함수를 쓴 쿼트 패턴
다형 함수를 쓴 쿼트 패턴 (Quoted Patterns with Polymorphic Functions)
쿼트 패턴(quoted patterns)에 다형 함수를 위한 타입 변수 처리를 더하는 실험적 기능이에요. 고차 패턴(higher-order pattern)에서 타입 변수를 사용할 수 있게 됩니다.
본문
이 기능은 쿼트 패턴의 능력을 다형 함수에 관해 확장해요. 아직 Scala 언어 표준의 일부는 아니에요. 이 기능을 쓰려면 언어 기능 experimental.quotedPatternsWithPolymorphicFunctions를 켜면 돼요. 언어 import로 할 수 있고
import scala.language.experimental.quotedPatternsWithPolymorphicFunctions
또는 커맨드라인 옵션 -language:experimental.quotedPatternsWithPolymorphicFunctions를 설정해도 돼요.
배경 (Background)
쿼트 패턴은 쿼트된 코드를 패턴으로 쓸 수 있게 해 줘요. 쿼트 패턴을 쓰면 어떤 표현식이 다른 것과 동등한지 확인하거나, 분해할 수 있어요. 특히 고차 패턴은 함수 본문 안의 코드 조각을 추출할 때 유용해요.
def decomposeFunc(x: Expr[Any])(using Quotes): Expr[Int] =
x match
case '{ (a: Int, b: Int) => $y(a, b) : Int } =>
'{ $y(0, 0) }
case _ => Expr(0)
위 예시에서 첫 번째 case는 x가 함수인 경우에 맞고, y는 함수의 본문에 묶여요. 고차 패턴 $y(a, b)는 변수 a와 b가 자유롭게 나타나는(code with free occurence) 어떤 코드와도 일치한다고 말해요. 만약 $y(a)라면 (a: Int, b: Int) => a + b 같은 표현식은 일치하지 않아요. a + b가 고차 패턴에 포함되지 않은 b의 등장을 가지니까요.
동기 (Motivation)
이 실험적 기능은 이 고차 패턴 문법을 타입 변수를 허용하도록 확장해요.
def decomposePoly(x: Expr[Any])(using Quotes): Expr[Int] =
x match
case '{ [A] => (x: List[A]) => $y[A](x) : Int } =>
'{ $y[Int](List(1, 2, 3)) }
case _ => Expr(0)
이제 타입 변수를 가진 고차 패턴 $y[A](x)를 쓸 수 있어요. y는 A와 x가 나타나는 코드의 본문에 묶이고, 타입은 [A] => (x: List[A]) => Int예요.
타입 의존성 (Type Dependency)
고차 패턴이, 쿼트 패턴에 정의된 타입 파라미터를 가진 타입의 값 파라미터를 지니면, 그 타입 파라미터들도 고차 패턴에서 캡처되어야 해요. 예를 들어 다음 패턴은 타입이 정해지지 않을 거예요.
case '{ [A] => (x: List[A]) => $y(x) : Int } =>
이 경우 x는 타입 List[A]를 가지는데, 여기에는 패턴에 정의된 타입 변수 A가 포함돼요. 하지만 고차 패턴 $y(x)는 어떤 타입 파라미터도 가지지 않아요. 이는 타입이 잘못되어야(ill-typed) 해요. $y[A](x)처럼 타입 파라미터를 추가하면 이런 종류의 타입 오류를 항상 피할 수 있어요.
구현 제약 (Implementation Restriction)
현재 구현은 경계(bounds)가 없는 타입 파라미터만 허용해요. 그런 패턴에 대한 건전한 타입 규칙이 아직 명확하지 않기 때문이에요.
case '{ [A] => (x: List[A]) => $y(x) : Int } => // Allowed
case '{ [A <: Int] => (x: List[A]) => $y(x) : Int } => // Disallowed