【问题标题】:Could not find implicit value inside singleton object在单例对象中找不到隐式值
【发布时间】:2017-03-16 11:04:01
【问题描述】:

我有这个代码:

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

但是当我尝试在单例对象中调用相同的代码时(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()

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

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

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

【问题讨论】:

  • 我没有答案给你,但为了清楚起见,你在这里与 Scala 作斗争。如果您将 Encoder 实现为类型类而不是依赖于路径的类型,事情会变得更加顺利,类型推断路径也会有更多的文档说明。
  • 似乎对以隐式参数形式出现的依赖于路径的类型有问题。
  • 我不是 100% 确定这里出了什么问题,但这确实可以在 Scala 2.12.0 中编译。
  • @Jasper-M 是的,它确实在 Scala 2.12.0 中编译。谢谢!所以这是 Scala 2.11.* 编译器问题。
  • 嗯,你的 Encoder 特征是 SAM,他们在 2.12 中改变了对 SAM 的处理。不确定这是否是它在 2.12 中编译并在 2.11 中出错的根本原因,但这是我的第一个直觉。

标签: scala implicit


【解决方案1】:

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

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

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

【讨论】:

  • 我认为不应该过多地使用路径依赖类型,而是。编写代码就像所有方法参数都是不变的一样是非常困难的。 AsyncEncoder 也是 Encoder。如果您总是需要指定所需的特定子类型,您将失去大部分 Encoder 的用处。
  • 我们在这里谈论的是最佳实践,还是观察到的行为背后的原因?
  • 两者,真的。如果路径相关的类型不在游戏中,则隐式会一致地解析。
  • 路径依赖类型只是一个用例——在过去,当它只是 implicitly[MyClass]implicit val mc = new MyClassImpl 的问题时,我遇到了更多关于隐式的问题。这就是为什么我怀疑这更多的是引擎的限制。
  • @MateuszKubuszok 似乎是 Scala 2.11.* 问题/限制,因为在 2.12.0 秒的情况下也可以按预期编译和运行。
【解决方案2】:

正如它在 cmets 中所说,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()
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-14
    • 2020-11-24
    • 1970-01-01
    • 2019-10-24
    • 1970-01-01
    • 2020-09-17
    • 2018-09-12
    • 1970-01-01
    相关资源
    最近更新 更多