Could not find implicit value inside singleton object

落花浮王杯 提交于 2019-12-10 19:07:28

问题


I have this code:

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 {
}

When I use it like this (case 1):

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

  import c._

  implicitly[Encoder].write()
}

then it compiles and prints Test.

But when I try to call the same code inside singleton object (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()
}

compilation fails with:

path/to/Main.scala:29: could not find implicit value for parameter e: c.Encoder implicitly[c.Encoder].write()

If I change (case 3):

implicit val e = new AsyncEncoder()

to

implicit val e: Encoder = new AsyncEncoder()

then it compiles and runs as expected.

But for some reason this is not acceptable for me.

Why does compilation fail in the above case?


回答1:


I would argue that the problem is not the fact that you use object, but that you are accepting ConcreteContext, as an argument: ConcreteContext.e's type is AsyncEncoder, not Encoder.

Many times I observed, that, when it comes to Scala, it is better to treat arguments as invariants unless specified otherwise (e.g. macwire often fails if you don't cast impl type to interface type - it is not completely predicatable though, most of the time it works).

As you observed setting e type explicitly to Encoder would solve the issue. So would changing ConcreteContext to Context. My guess is that it is either a matter of invariance or limitation of a compiler's type inference engine.




回答2:


As it said in comments there is no issue in Scala 2.12.0.

For Scala 2.11.8 I used the following workaround assuming that Encoder will have only one method:

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()
}


来源:https://stackoverflow.com/questions/40391732/could-not-find-implicit-value-inside-singleton-object

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!