E108: Unapply Invalid Return Type

E108: Unapply Invalid Return Type (unapply 반환 타입이 잘못됨)

추출자의 unapply 또는 unapplySeq 메서드의 반환 타입이 유효하지 않을 때 나오는 에러예요.

출처: Scala 3 Reference

본문

추출자로 사용하려면 unapply 메서드가 다음 중 하나에 해당하는 타입을 반환해야 해요.

  • isEmpty: Booleanget: S 멤버를 가진 타입 (보통 Option[S])
  • Boolean
  • Product (예: Tuple2[T1, T2])

예시

object MyExtractor:
  def unapply(s: String): String = s

def example(s: String) = s match
  case MyExtractor(_) => "ok"
  case _ => "no match"

에러 메시지

-- [E108] Declaration Error: example.scala:5:18 --------------------------------
5 |  case MyExtractor(_) => "ok"
  |       ^^^^^^^^^^^^^^
  |   String is not a valid result type of an unapply method of an extractor.
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  |
  | To be used as an extractor, an unapply method has to return a type that either:
  |  - has members isEmpty: Boolean and get: S (usually an Option[S])
  |  - is a Boolean
  |  - is a Product (like a Tuple2[T1, T2]) of arity i with i >= 1, and has members _1 to _i
  |
  | See: https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html#fixed-arity-extractors
  |
  | Examples:
  |
  | class A(val i: Int)
  |
  | object B {
  |   def unapply(a: A): Option[Int] = Some(a.i)
  | }
  |
  | object C {
  |   def unapply(a: A): Boolean = a.i == 2
  | }
  |
  | object D {
  |   def unapply(a: A): (Int, Int) = (a.i, a.i)
  | }
  |
  | object Test {
  |   def test(a: A) = a match {
  |     case B(1) => 1
  |     case a @ C() => 2
  |     case D(3, 3) => 3
  |   }
  | }
  |
   -----------------------------------------------------------------------------

해결 방법

용도에 따라 Option, Boolean, 또는 튜플을 반환하도록 바꾸면 돼요.

// Return Option[T] for single value extraction
object MyExtractor:
  def unapply(s: String): Option[String] = Some(s)

def example(s: String) = s match
  case MyExtractor(x) => x
  case _ => "no match"
// Or return Boolean for test-only extraction
object IsEmpty:
  def unapply(s: String): Boolean = s.isEmpty

def example(s: String) = s match
  case IsEmpty() => "empty"
  case _ => "not empty"
// Or return a tuple for multiple values
object Split:
  def unapply(s: String): Option[(String, String)] =
    val mid = s.length / 2
    Some((s.take(mid), s.drop(mid)))

def example(s: String) = s match
  case Split(a, b) => s"$a | $b"
  case _ => "no match"

더 알아보기