2017-02-11 1 views
4

다음 코드에서는 셰이프가없는 typeclass 인스턴스를 파생하려고합니다. 그러나 더 복잡한 HList로 변환되는 더 복잡한 사례 클래스의 경우 컴파일러에서 같은 종류의 암시 적 형식을 두 번 해결하지는 않지만 "확장 된 암시 적 확장"을 제공합니다. 어쩌면 나는 컴파일러의 다른 규칙을 놓치고 있습니까?왜 scalac은 여기에서 "발산하는 암시 적 확장"오류를 발생합니까?

(바이올린 : https://scalafiddle.io/sf/WEpnAXN/0)는

import shapeless._ 

trait TC[T] 

sealed trait Trait1 
case class SimpleClass(a: String) extends Trait1 

sealed trait Trait2 
case class ComplexClass(a: String, b: String) extends Trait2 

object Serialization extends App { 

    //Instances for HList 
    implicit val hnilInstance: TC[HNil] = ??? 
    implicit def hconsInstance[H, T <: HList] (implicit t: TC[T]): TC[H :: T] = ??? 

    //Instances for CoProduct 
    implicit val cnilInstance: TC[CNil] = ??? 
    implicit def cconsInstance[H, T <: Coproduct] (implicit h: TC[H], t: TC[T]): TC[H :+: T] = ??? 

    //Instances for Generic, relying on HNil & HCons 
    implicit def genericInstance[T, H] (implicit g: Generic.Aux[T, H], t: TC[H]): TC[T] = ??? 

    the[TC[SimpleClass :+: CNil]] //Works 
    the[TC[Trait1]]    //Works 
    the[TC[ComplexClass :+: CNil]] //Works 
    the[TC[Trait2]]    //Fails with diverging implicit expansion 
} 

것은 그런 식으로 뭔가를해야 the[TC[Trait1]] 컴파일러를 해결하려고 할 때 :

TC[Trait1] 
    Generic[Trait1] 
    TC[SimpleClass :+: CNil] 
     TC[SimpleClass] 
      Generic[SimpleClass] 
      TC[String :: HNil] 
     TC[CNil] 

작동하는 것 같다있다. 그러나 2-field case 클래스를 사용하면 컴파일러가 이와 같은 작업을 수행하지 못합니다. 따라서 궁금한 점이 있습니다. 이제는 Lazy을 사용해야 만 왜 작동합니까?

TC[Trait2] 
    Generic[Trait2] 
    TC[ComplexClass :+: CNil] 
     TC[ComplexClass] 
      Generic[ComplexClass] 
      TC[String :: String :: HNil] 
     TC[CNil] 

나는 거기에 코드를 실행할 수 있도록 약간의 바이올린을 만들었습니다.

+1

나는이 질문에서 내 케이스와 정확히 같지는 않지만 마일즈의 대답이 (http://stackoverflow.com/a/27911353/334519)라고 생각합니다. –

답변

6

몇 년 전 some issues like this을 통해 작업했을 때 나는 분기 검사기가 무엇을하고 있었는지 알아내는 가장 쉬운 방법은 컴파일러에 println을 던져서 로컬로 게시하는 것이 었습니다.

overlaps(dtor1, dted1) && (dtor1 =:= dted1 || { 
    val dtorC = complexity(dtor1) 
    val dtedC = complexity(dted1) 
    val result = dtorC > dtedC 

    println(if (result) "Dominates:" else "Does not dominate:") 
    println(s"$dtor (complexity: $dtorC)") 
    println(s"$dted (complexity: $dtedC)") 
    println("===========================") 
    result 
}) 

그리고 우리가 할 수있는 sbt publishLocal scalac하여 코드를 컴파일하려고 : 2.12에서 관련 코드는 우리가 이런 일에 마지막 줄을 대체 할 수 dominates 방법 here이며,

Dominates: 
TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7) 
TC[shapeless.:+:[ComplexClass,shapeless.CNil]] (complexity: 6) 
=========================== 

여기서 문제는 String :: String :: HNil (트리의 최하위 노드)에 TC 인스턴스를 찾고 있지만 ComplexClass :+: CNil (3 단계 위로)에 대한 공개 검색이 있다는 것입니다. 컴파일러는 String :: String :: HNil이 겹치고 모두 ComplexClass :+: CNil을 지배한다고 생각합니다. 재귀 적으로 보이기 때문에 컴파일러는이를 보완합니다.

이렇게 말도 안되는 소리가 들리므로, 우리는 공산품 부품에 약간의 복잡성을 추가하고 어떤 일이 발생하는지 직접 확인함으로써 스스로를 설득하려고 시도 할 수 있습니다. 그냥 생성자를 추가하자

case class Foo(i: Int) extends Trait2 

이제 모든 것이 잘 작동, 우리는 컴파일시에이 메시지가 :

Does not dominate: 
TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7) 
TC[shapeless.:+:[ComplexClass,shapeless.:+:[Foo,shapeless.CNil]]] (complexity: 9) 

는 그래서 ComplexClass hlist 표현이 여전히 Trait2 부산물 표현을 겹치는하지만 아무튼 Trait2 표현 (우리가 걱정하고있는 공개 암시 TC 검색의 주제)이 이제는 더 복잡해지기 때문에 그것을 지배하지 못합니다.

검사기가 명확히 여기 너무 편집증 적이면서 그 행동이 may change in the future인데, 지금은 그걸로 우리는 붙어 있습니다. 가장 간단하고 어리석은 해결책은 Lazy을 거기에 붙이면 발산 검사에서 재귀를 숨길 수 있다는 것입니다.이 경우 특별히하지만, 그것은 단지 TC 동반자 객체의 인스턴스를 넣어 모양에서

은 잘 작동 : 다음

import shapeless._ 

sealed trait Trait1 
case class SimpleClass(a: String) extends Trait1 

sealed trait Trait2 
case class ComplexClass(a: String, b: String) extends Trait2 

trait TC[T] 

object TC { 
    //Instances for HList 
    implicit def hnilInstance: TC[HNil] = ??? 
    implicit def hconsInstance[H, T <: HList](implicit t: TC[T]): TC[H :: T] = ??? 

    //Instances for CoProduct 
    implicit def cnilInstance: TC[CNil] = ??? 
    implicit def cconsInstance[H, T <: Coproduct](implicit 
    h: TC[H], t: TC[T] 
): TC[H :+: T] = ??? 

    //Instances for Generic, relying on HNil & HCons 
    implicit def genericInstance[T, H](implicit 
    g: Generic.Aux[T, H], t: TC[H] 
): TC[T] = ??? 
} 

그리고 :

Does not dominate: 
TC[shapeless.::[String,shapeless.::[String,shapeless.HNil]]] (complexity: 7) 
TC[shapeless.:+:[ComplexClass,shapeless.CNil]] (complexity: 16) 

왜 주위에 같은 물건을 이동 않습니다 이것은 제품의 복잡성을 높여 줍니까? 나는 모른다.

+1

이러한 종류의 오류를 만드는 티켓은 더 많은 것을 스스로 설명합니다. https://issues.scala-lang.org/browse/SI-8467 –

+0

@SethTisue와 같은 문제는 보지 못했지만 '-Xlog-implicits'의 결과를 출력하고, 현재의 메시지의보다 특정한 하위 집합을 가지고 있다면, 이와 같은 이상한 분기 사건을 실제로 도울 수는 없습니다. –

+0

아주 좋은 explaination! 컴파일러가 왜 편집증인지 이해하지 못하는 것 같지만 적어도 실패 지점은 이제 매우 분명합니다. 고맙습니다! – valenterry

관련 문제