无法在单例对象中找到隐含值

时间:2016-11-02 23:55:51

标签: scala implicit

我有这段代码:

trait Context {
  implicit val e: Encoder

  trait Encoder {
    def write(): Unit = {
      println("Test")
    }
  }

}

trait AsyncEncoders {
  this: Context =>

  class AsyncEncoder extends Encoder {
  }

  implicit val e = new AsyncEncoder()
}

class ConcreteContext extends Context with AsyncEncoders {
}

当我像这样使用它时(案例1 ):

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

  import c._

  implicitly[Encoder].write()
}

然后它编译并打印Test

但是当我尝试在singleton对象中调用相同的代码时( case 2 ):

object TestObject {
  def apply()(implicit c: ConcreteContext): Unit = {
    import c._
    implicitly[Encoder].write()
  }
}

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

  TestObject()
}

编译失败:

  

path / to / Main.scala:29:找不到参数e的隐含值:c.Encoder        隐式[c.Encoder] .WRITE()

如果我改变了(案例3 ):

implicit val e = new AsyncEncoder()

implicit val e: Encoder = new AsyncEncoder()

然后它按预期编译并运行。

但由于某些原因,这对我来说是不可接受的。

为什么在上述情况下编译失败?

2 个答案:

答案 0 :(得分:0)

我认为问题不在于您使用对象,而在于您接受ConcreteContext作为参数:ConcreteContext.e的类型是AsyncEncoder,而不是{ {1}}。

我经常观察到,当谈到Scala时,除非另有说明,否则最好将参数视为不变量(例如,如果不将impl类型转换为接口类型,macwire通常会失败 - 尽管它不是完全可预测的,大部分时间都有效。)

正如您观察到将{e}类型明确设置为Encoder可以解决问题。因此会将Encoder更改为ConcreteContext。我的猜测是,这是编译器类型推理引擎的不变性或限制问题。

答案 1 :(得分:0)

正如评论中所述,Scala 2.12.0中没有问题。

对于Scala 2.11.8,假设Encoder只有一个方法,我使用了以下解决方法:

trait Context {
  implicit val e: Encoder

  type BaseEncoder = () => Unit

  type Encoder <: BaseEncoder
}

trait AsyncEncoders {
  this: Context =>

  type Encoder = AsyncEncoder

  class AsyncEncoder extends BaseEncoder {
    override def apply(): Unit = {
      println("Test")
    }
  }

  implicit val e = new AsyncEncoder()
}

class ConcreteContext extends Context with AsyncEncoders {
}

object TestObject {
  def apply()(implicit c: ConcreteContext): Unit = {
    import c._
    implicitly[Encoder].apply()
  }
}

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

  TestObject()
}