E127: Not An Extractor — 추출기(extractor)가 아닌 타입

E127: Not An Extractor — 추출기(extractor)가 아닌 타입

패턴 매칭에서 타입을 추출기로 사용하려고 하는데, 그 타입에 알맞은 unapply 또는 unapplySeq 메서드가 없을 때 발생하는 에러예요.

출처: Scala 3 Reference

본문

패턴 매칭에서 어떤 타입을 추출기로 사용하려고 하는데, 그 타입에 알맞은 unapply 또는 unapplySeq 메서드가 없을 때 발생해요.

Scala에서 case Foo(x)처럼 추출기 문법으로 패턴 매칭을 하려면, 해당 타입에 unapply 또는 unapplySeq 메서드가 있어야 해요. case 클래스는 이런 메서드를 자동으로 얻지만, 일반 클래스는 그렇지 않아요.

unapply 메서드는 object 안에 있어야 하고, 하나의 명시적 term 파라미터를 받아야 하며, 다음과 같은 형태 중 하나여야 해요.

또한 unapplyunapplySeq 메서드는 명시적 term 파라미터 뒤에 타입 파라미터를 가질 수 없어요.

가끔은 하위 값의 개수가 고정되어 있지 않을 때가 있어요. 그럴 땐 시퀀스를 반환하고 싶을 텐데요, 그래서 unapplySeq로 패턴을 정의할 수도 있어요. unapplySeqOption[Seq[T]]을 반환해요. 이 메커니즘은 예를 들어 case List(x1, ..., xn) 패턴에 사용돼요.

예시

class Box(val x: Int)

def example(a: Any) = a match
  case Box(x) => ()

에러

-- [E127] Pattern Match Error: example.scala:4:7 -------------------------------
4 |  case Box(x) => ()
  |       ^^^
  |Box cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method with the appropriate signature
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  | An unapply method should be in an object, take a single explicit term parameter, and:
  |   - If it is just a test, return a Boolean. For example case even()
  |   - If it returns a single sub-value of type T, return an Option[T]
  |   - If it returns several sub-values T1,...,Tn, group them in an optional tuple Option[(T1,...,Tn)]
  |
  | Additionaly, unapply or unapplySeq methods cannot take type parameters after their explicit term parameter.
  |
  | Sometimes, the number of sub-values isn't fixed and we would like to return a sequence.
  | For this reason, you can also define patterns through unapplySeq which returns Option[Seq[T]].
  | This mechanism is used for instance in pattern case List(x1, ..., xn)
   -----------------------------------------------------------------------------

해결 방법

case 클래스는 unapply 메서드를 자동으로 제공하니까, case 클래스로 바꾸면 돼요.

// Use a case class which automatically provides an unapply method
case class Box(x: Int)

def example(a: Any) = a match
  case Box(x) => x
  case _ => 0

아니면 동반 객체(companion object)에 unapply 메서드를 직접 정의할 수도 있어요.

// Alternative: Define an explicit companion object with an unapply method
class Box(val x: Int)

object Box:
  def unapply(box: Box): Option[Int] = Some(box.x)

def example(a: Any) = a match
  case Box(x) => x
  case _ => 0