使用类型参数进行隐式解析时,为什么 val 放置很重要?

When doing implicit resolution with type parameters, why does val placement matter?

在一个文件中,我有:

trait JsonSchema[T] {
  val propertyType: String

  override def toString: String = propertyType
}

object JsonSchema {
  implicit def stringSchema: JsonSchema[String] = new JsonSchema[String] {
    override val propertyType: String = "string"
  }

  implicit def intSchema: JsonSchema[Int] = new JsonSchema[Int] {
    override val propertyType: String = "integer"
  }

  implicit def booleanSchema: JsonSchema[Boolean] = new JsonSchema[Boolean] {
    override val propertyType: String = "boolean"
  }
}

在我的主文件中:

case class MetaHolder[T](v: T)(implicit val meta: JsonSchema[T])

object JsonSchemaExample extends App {
  println(MetaHolder(3).meta.toString)
  println(MetaHolder("wow").meta.toString)
}

非常有效。现在假设我这样做:

case class MetaHolder[T](v: T) {
  val meta: JsonSchema[T] = implicitly[JsonSchema[T]]
}

它不再编译。为什么?

我的目标是通过向所有内容添加 val meta 来修改 scala Finch 库中的匿名 Endpoint 类。到目前为止,我已经能够在没有任何花哨业务的情况下做到这一点,但现在我想用 shapeless 做一些花哨的隐式解析,为任意情况 类 提供 JsonSchema 定义。我的问题是如何在保持向后兼容性的同时做到这一点。如:为想要选择加入的人提供 jsonschema 元特性,不要为不想使用元的人改变编译负担,

如果我改用第一条路线,添加一个隐式参数,是否需要每个人都添加一个特殊的导入?还是我遗漏了一些东西,是否仍会保持向后兼容性?

为了让 implicitly[JsonSchema[T]] 编译,隐式作用域中必须有一个 JsonSchema[T],这意味着必须有一个 JsonSchema[T](或者可以隐式转换为 JsonSchema[T]) 作为隐式参数传递,就像您使用的那样:

case class MetaHolder[T](v: T)(implicit val meta: JsonSchema[T])

参数之间的implicit x: X和body内部的implicitly[X]差别很大

当您说 implicitly[X] 时,这意味着 "check now whether in the current scope there is an implicit X"。

当您说 def foo(...)(implicit x: X) = ... 时,这意味着 "check later when foo is called that in the scope of the call site there will be an implicit X (and for now inside foo just assume without checking that there is)"。

class Foo(...)(implicit x: X)类似于后者,"check when constructor is called that there will be an implicit X".

关于用户是否必须导入。如果您将类型 X 的隐式放入 X 的伴随对象,那么它们将被自动找到(类型 X[Y] 的隐式应该放入 XY).如果将它们放在其他地方,则必须将它们导入当前范围。