E142: Skolem in Inferred — 추론에서의 스콜렘 타입

E142: Skolem in Inferred — 추론에서의 스콜렘 타입

컴파일러가 스콜렘 타입(skolem type)을 포함하는 given 인스턴스를 생성하려고 했을 때 발생했던 에러예요.

출처: Scala 3 Reference

본문

참고: 이 에러 코드는 Scala 3.8.1에서 비활성화되었어요. 동작이 컴파일 과정에서 더 일찍 TypeError를 던지는 쪽으로 바뀌면서, 잘못된 트리 생성 자체를 막게 되었어요.

이 에러는 컴파일러가 스콜렘 타입에 대한 참조를 포함하는 given 인스턴스를 생성하려고 할 때 발생했어요. 스콜렘 타입은 타입 추론 중에 컴파일러가 내부적으로 사용하는 자리표시자 타입인데, 생성된 코드에서는 직접 참조할 수 없어요.

이 동작을 바꾼 커밋에 따르면: "이전에는 valueOf 인라인 호출이 성공했는데(ValueOf 합성기가 tpd.ref를 호출하고, tpd.reftpd.singleton을 호출하기 때문), 그 결과 생성된 유효하지 않은 트리가 백엔드에서 'assertion failed: Cannot create ClassBType from NoSymbol'로 크래시를 냈다. tpd.singletonTypeError를 던지도록 고쳤다."

에러 메시지는 암시적 탐색이 해결책을 찾았지만, 그 해결책에 실제 코드로 구체화할 수 없는 스콜렘 타입을 참조하는 부분이 들어 있다는 뜻이었어요.

예시

trait QC:
  object tasty:
    type Tree
    extension (tree: Tree)
      def pos: Tree = ???

def test =
  given [T]: QC = ???
  def unseal(using qctx: QC): qctx.tasty.Tree = ???
  unseal.pos

에러

-- [E142] Type Error: example.scala:10:2 ---------------------------------------
10 |  unseal.pos
   |  ^^^^^^
   |Failure to generate given instance for type ?{ pos: ? } from argument of type ?1.tasty.Tree)
   |
   |I found: <skolem>.tasty.pos(unseal(given_QC[Any]))
   |But the part corresponding to `<skolem>` is not a reference that can be generated.
   |This might be because resolution yielded as given instance a function that is not
   |known to be total and side-effect free.
   |
   |where:    ?1 is an unknown value of type QC
   |----------------------------------------------------------------------------
   | Explanation (enabled by `-explain`)
   |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   | The part of given resolution that corresponds to `<skolem>` produced a term that
   | is not a stable reference. Therefore a given instance could not be generated.
   |
   | To trouble-shoot the problem, try to supply an explicit expression instead of
   | relying on implicit search at this point.
    ----------------------------------------------------------------------------

참고: Scala 3.8.1 이상에서는 이 에러가 컴파일 과정에서 더 일찍 TypeError를 던지는 방식으로 바뀌어서, E008 에러가 대신 발생해요.

해결 방법

이 에러를 푸는 핵심은 컨텍스트 파라미터를 명시적이고 안정적으로 만들어 스콜렘 타입이 생기는 걸 피하는 거예요.

trait QC:
  object tasty:
    type Tree
    extension (tree: Tree)
      def pos: Tree = ???

// Solution 1: Use an explicit named given instead of polymorphic given
def test =
  given qc: QC = ???  // Named given, not [T]: QC
  def unseal(using qctx: QC): qctx.tasty.Tree = ???
  val tree = unseal(using qc)
  qc.tasty.pos(tree)  // Call extension method explicitly with stable context
trait QC:
  object tasty:
    type Tree
    extension (tree: Tree)
      def pos: Tree = ???

// Solution 2: Store result with explicit type annotation
def test =
  given qc: QC = ???
  val tree: qc.tasty.Tree = {
    def unseal(using qctx: QC): qctx.tasty.Tree = ???
    unseal
  }
  tree.pos  // Now the type is stable
trait QC:
  object tasty:
    type Tree
    extension (tree: Tree)
      def pos: Tree = ???

// Solution 3: Make the context parameter explicit in the function signature
def test(using qc: QC) =  // Explicit context parameter
  def unseal(using qctx: QC): qctx.tasty.Tree = ???
  unseal.pos  // Works because qc is stable