2017-02-11 8 views
4

次のコードでは、シェイプレスでtypeclassインスタンスを導出しようとしています。しかし、より複雑なケースクラス(より複雑なHListに変換される)の場合、同じ種類の暗黙の型を2度解決しないように見えますが、コンパイラは私に "多様な暗黙の拡張"を与えます。たぶん私はコンパイラのいくつかの他のルールが欠けているでしょうか?スカラックはなぜここに「多様な暗黙的な拡張」エラーを起こすのですか?

(フィドル: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フィールドのケースクラスでは、コンパイラはこのようなことをすることができません - なぜ私はそれを動作させるためにここで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をコンパイラに投げてローカルに公開することでした。 2.12では、関連するコードは、我々はこのようなもので、最後の行を置き換えることができますdominates方法here、次のとおりです。

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をすることができますし、あなたのコードをコンパイルしよう:

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副生成物の表現と重なるが、それはdoesnの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

関連する問題