问题
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