2016-12-05 165 views
2

我有以下代碼:進口implicits作爲方法參數傳遞VS類的構造函數參數

case class Custom(value: Int) 
case class Custom2(value: Float) 

case class MappedEncoding[I, O](f: I => O) 

trait Decoders { 
    type BaseDecoder[T] =() => T 
    type Decoder[T] <: BaseDecoder[T] 
} 

trait ConcreteDecoders extends Decoders { 
    type Decoder[T] = ConcreteDecoder[T] 

    case class ConcreteDecoder[T](decoder:() => T) extends BaseDecoder[T] { 
    def apply(): T = decoder() 
    } 

    implicit def optionDecoder[T](implicit d: Decoder[T]): Decoder[Option[T]] = 
    ConcreteDecoder[Option[T]](() => Some(d())) 

    implicit def mappedDecoder[I, O](implicit mapped: MappedEncoding[I, O], decoder: Decoder[I]): Decoder[O] = 
    ConcreteDecoder[O](() => mapped.f(decoder())) 

    implicit def intDecoder: Decoder[Int] = ConcreteDecoder[Int](() => 1) 
    implicit def floatDecoder: Decoder[Float] = ConcreteDecoder(() => 1) 

} 

class ConcreteContext extends ConcreteDecoders { 
} 

case class TestObject() { 

    implicit val customDecoder = MappedEncoding[Int, Custom](Custom) 
    implicit val custom2Encoder = MappedEncoding[Custom2, Float](_.value) // 1 
    implicit val custom2Decoder = MappedEncoding[Float, Custom2](Custom2) 

    def a(c: ConcreteContext): Unit = { 
    import c._ 
    implicitly[Decoder[Option[Custom]]] // 2 
// implicitly[Decoder[Float]]   // 3 
    implicitly[Decoder[Option[Float]]] 
    () 
    } 
} 


object Main extends App { 
    implicit val c = new ConcreteContext() 

    TestObject().a(c) 
    // TestObject(a).() 
} 

而且它不會在斯卡拉2.11.8編譯和2.12.0錯誤:

diverging implicit expansion for type c.Decoder[Option[Float]] 
[error] starting with method intDecoder in trait ConcreteDecoders 
[error]  implicitly[Decoder[Option[Float]]] 

使用-Xlog-implicits選項提供長時間的輸出,但最有趣的部分是:

floatDecoder is not a valid implicit value for c.Decoder[Float] because: 
[info] diverging implicit expansion for type c.Decoder[T] 
[info] starting with method intDecoder in trait ConcreteDecoders 
[info]  implicitly[Decoder[Option[Float]]] 

c: CustomContext從方法參數移動到case類構造函數參數使其編譯。我認爲這可能會改變暗示搜索範圍。

此外下列動作中的一個使其編譯:

  • 評論線標有註解1(==第1行)
  • 註釋線2
  • 取消註釋線3

它看起來像解決implicitly[Decoder[Option[Custom]]]葉斯卡拉編譯器處於影響解決implicitly[Decoder[Option[Float]]]的狀態。

爲什麼會發生這種情況,如何在不移動c: ConcreteContext的方法參數的情況下編譯它?

P.S.這是重現問題的簡化代碼。真正的代碼要複雜得多,我需要支持ConcreteContext作爲方法參數傳遞的情況。

+0

的東西在這裏肯定有鬼。這似乎與[SI-9625](https://issues.scala-lang.org/browse/SI-9625)有些相關。將方法參數變爲構造函數參數時,該問題也會消失。在旁註中,將'Decoder'作爲'Function0'的子類型可能也不是最好的想法。 –

+0

@ Jasper-M由於http://stackoverflow.com/q/40391732/746347,我使'Decoder'成爲'Function0'的一個子類型。 – mixel

+1

顯而易見的解決方法'val c0:c.type = c;在def a(c:ConcreteContext):Unit'裏面導入c0._'就足夠滿足你的需求了? –

回答

1

這不是一個完整的答案。

我還沒有任何令人滿意的解釋,爲什麼提到另一個隱式可能會導致分辨率退出循環,由mappedEncodercustom2Encoder + custom2Decoder對創建。我只能猜測implicitly[Decoder[Float]]的存在正在提高floatDecoder的優先級而不是指定的custom2...循環。

但有一個很好的解決方案,這可能不是最好的,但可行的選項,被稱爲 shapeless.Lazy。它有時可以用來代替LowPriority分解來處理像這樣的更可怕的情況。

您只需重寫mappedDecoder作爲

import shapeless.Lazy 

implicit def mappedDecoder[I, O] 
    (implicit mapped: MappedEncoding[I, O], 
      decoder: Lazy[Decoder[I]]): Decoder[O] = 
    ConcreteDecoder[O](() => mapped.f(decoder.value())) 

,使原來的代碼工作

相關問題