Scala 在singleton对象中找不到隐式值

Scala 在singleton对象中找不到隐式值,scala,implicit,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 AsyncEn

我有以下代码:

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):

然后它编译并打印
Test

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

编译失败,原因是:

path/to/Main.scala:29:找不到参数e:c的隐式值。编码器 隐式[c.Encoder].write()

如果我改变(案例3):

然后它按照预期编译和运行

但出于某种原因,这对我来说是不可接受的


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

我认为问题不在于您使用object,而在于您接受
ConcreteContext
作为参数:
ConcreteContext.e
的类型是
AsyncEncoder
,而不是
Encoder

我多次观察到,当涉及Scala时,最好将参数视为不变量,除非另有规定(例如,如果不将impl类型强制转换为接口类型,macwire通常会失败-但在大多数情况下,它不是完全可预测的)


正如您所观察到的,将e type显式设置为
Encoder
将解决此问题。将
ConcreteContext
更改为
Context
也是如此。我的猜测是,这要么是编译器类型推理引擎的不变性问题,要么是它的局限性问题。

正如它在评论中所说,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()
}
trait上下文{
隐式vale:编码器
类型BaseEncoder=()=>单元
类型编码器
类型编码器=异步编码器
类AsyncEncoder扩展了BaseEncoder{
覆盖def apply():单位={
println(“测试”)
}
}
隐式val e=新的AsyncEncoder()
}
类ConcreteContext使用异步编码器扩展上下文{
}
对象测试对象{
def apply()(隐式c:ConcreteContext):单位={
进口c_
隐式[Encoder].apply()
}
}
对象主应用程序{
隐式val c=new-ConcreteContext()
TestObject()
}

我没有答案给你,但我要明确一点,你在与Scala抗争。如果您将
Encoder
实现为一个类型类而不是路径依赖类型,那么事情会更顺利,类型推断路径也会有更好的文档记录。对于以隐式参数形式出现的路径依赖类型,似乎存在问题。我不确定这里到底出了什么问题,但是它在Scala2.12.0中编译。@Jasper-M是的,它在Scala2.12.0中编译。谢谢这就是Scala 2.11.*编译器问题。嗯,你的
编码器
特性是SAM,他们在2.12中改变了SAM的处理方式。不确定这是否是它在2.12中编译和在2.11中出错的根本原因,但这是我的第一个直觉。我认为不应该过多地使用路径依赖类型。如果所有方法参数都是不变的,那么编写代码将非常困难。
异步编码器
也是
编码器
。如果您总是需要指定所需的特定子类型,那么您将失去编码器的大部分有用性。我们在这里讨论的是最佳实践,还是观察到的行为背后的原因?两者都有。如果路径依赖类型不起作用,则隐式解析将保持一致。路径依赖类型只是一个用例-过去我在隐式方面遇到了更多问题,当时只是
隐式[MyClass]
vs
隐式val mc=new MyClassImpl
。这就是为什么我怀疑这更多的是引擎的局限性。@MateuszKubuszok似乎是Scala 2.11.*问题/局限性,因为在2.12.0中,第二种情况也会按预期编译和运行。
object TestObject {
  def apply()(implicit c: ConcreteContext): Unit = {
    import c._
    implicitly[Encoder].write()
  }
}

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

  TestObject()
}
implicit val e = new AsyncEncoder()
implicit val e: Encoder = new AsyncEncoder()
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()
}