패턴 바인딩
패턴 바인딩 (Pattern Bindings)
Scala 2에서는 val 정의와 for 식 안의 패턴 바인딩이 느슨하게 타입 검사됐어요. 실패할 수도 있는 매치가 여전히 컴파일 타임에 받아들여졌지만, 프로그램의 런타임 동작에 영향을 줄 수 있었죠. Scala 3.2부터는 타입 검사 규칙이 더 엄격해져서, 대신 컴파일 타임에 경고가 보고돼요.
본문
패턴 정의에서의 바인딩
val xs: List[Any] = List(1, 2, 3)
val (x: String) :: _ = xs // error: pattern's type String is more specialized
// than the right-hand side expression's type Any
이 코드는 Scala 3.2에서(그리고 -source future 설정 아래의 이른 Scala 3.x에서도) 컴파일 타임 경고를 내요. 반면 Scala 2에서는 런타임에 ClassCastException으로 실패해요. Scala 3.2에서 패턴 바인딩은 그 패턴이 반박 불가능(irrefutable)할 때만, 즉 오른쪽의 타입이 패턴의 타입에 부합할 때만 허용돼요. 예를 들어 다음은 괜찮아요.
val pair = (1, true)
val (x, y) = pair
가끔은 패턴이 반박 가능(refutable)하더라도 데이터를 분해하고 싶을 때가 있어요. 예를 들어 어떤 시점에 리스트 elems가 비어 있지 않다는 걸 안다면, 이렇게 분해하고 싶을 수 있죠.
val first :: rest = elems // error
이건 Scala 2에서는 동작해요. 사실 Scala 2 규칙의 전형적인 사용처죠. 하지만 Scala 3.2에서는 경고가 나요. 오른쪽에 @unchecked 애너테이션을 붙이면 경고를 피할 수 있어요.
val first :: rest = elems: @unchecked // OK
이렇게 하면 컴파일러가 패턴 바인딩을 받아들여요. elems가 결코 비어 있지 않다는 가정이 틀렸다면, 대신 런타임에 오류가 날 수도 있어요.
for 식에서의 패턴 바인딩
비슷한 변경이 for 식의 패턴에도 적용돼요. 예를 들어볼게요.
val elems: List[Any] = List((1, 2), "hello", (3, 4))
for (x, y) <- elems yield (y, x) // error: pattern's type (Any, Any) is more specialized
// than the right-hand side expression's type Any
이 코드는 Scala 3.2에서 컴파일 타임 경고를 내요. 반면 Scala 2에서는 리스트 elems를 걸러내서 패턴 (x, y)에 맞는 튜플 타입 요소들만 남겨요. Scala 3에서 이 필터링 기능을 얻으려면 패턴 앞에 case를 붙이면 돼요.
for case (x, y) <- elems yield (y, x) // returns List((2, 1), (4, 3))
문법 변경 (Syntax Changes)
for 식의 생성자(generator)는 case를 접두어로 붙일 수 있어요.
Generator ::= [‘case’] Pattern1 ‘<-’ Expr
마이그레이션 (Migration)
새 문법은 Scala 3.0에서 지원돼요. 하지만 Scala 2와 Scala 3 사이의 원활한 크로스 컴파일을 위해, 변경된 동작과 추가 타입 검사는 -source future 설정 아래에서만 활성화돼요. 언어 버전 3.2에서는 기본으로 켜질 예정이에요.