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