E142: Skolem in Inferred — 추론에서의 스콜렘 타입
E142: Skolem in Inferred — 추론에서의 스콜렘 타입
컴파일러가 스콜렘 타입(skolem type)을 포함하는 given 인스턴스를 생성하려고 했을 때 발생했던 에러예요.
본문
참고: 이 에러 코드는 Scala 3.8.1에서 비활성화되었어요. 동작이 컴파일 과정에서 더 일찍
TypeError를 던지는 쪽으로 바뀌면서, 잘못된 트리 생성 자체를 막게 되었어요.
이 에러는 컴파일러가 스콜렘 타입에 대한 참조를 포함하는 given 인스턴스를 생성하려고 할 때 발생했어요. 스콜렘 타입은 타입 추론 중에 컴파일러가 내부적으로 사용하는 자리표시자 타입인데, 생성된 코드에서는 직접 참조할 수 없어요.
이 동작을 바꾼 커밋에 따르면: "이전에는 valueOf 인라인 호출이 성공했는데(ValueOf 합성기가 tpd.ref를 호출하고, tpd.ref는 tpd.singleton을 호출하기 때문), 그 결과 생성된 유효하지 않은 트리가 백엔드에서 'assertion failed: Cannot create ClassBType from NoSymbol'로 크래시를 냈다. tpd.singleton이 TypeError를 던지도록 고쳤다."
에러 메시지는 암시적 탐색이 해결책을 찾았지만, 그 해결책에 실제 코드로 구체화할 수 없는 스콜렘 타입을 참조하는 부분이 들어 있다는 뜻이었어요.
예시
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