问题
In one file, I have:
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"
}
}
In my main file:
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)
}
That works hunky-dory. Now suppose I do this instead:
case class MetaHolder[T](v: T) {
val meta: JsonSchema[T] = implicitly[JsonSchema[T]]
}
It no longer compiles. Why?
My goal is to modify the anonymous Endpoint
classes in the scala Finch library by adding a val meta
to everything. I've been able to do this without any fancy-business so far, but now I want to do some fancy implicit resolution with shapeless to provide a JsonSchema
definition for arbitrary case classes. My question is how to do this while maintaining backward compatibility. As in: provide the jsonschema meta feature for people who want to opt in, don't change the compilation burden for anyone who does not want to use meta,
If instead I go the first route, with an added implicit parameter, wouldn't that require a special import to be added by everyone? Or am I missing something and would backward compatibility still be maintained?
回答1:
There is big difference between implicit x: X
among parameters and implicitly[X]
inside body.
When you say implicitly[X]
this means "check now whether in the current scope there is an implicit X
".
When you say def foo(...)(implicit x: X) = ...
this means "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)
is similar to the latter, "check when constructor is called that there will be an implicit X
".
Regarding whether users have to import or not. If you put implicits for type X
to companion object of X
then they will be found automatically (implicits for type X[Y]
should be put to companion object of either X
or Y
). If you put them somewhere else then they have to be imported to the current scope.
回答2:
In order for implicitly[JsonSchema[T]]
to compile, there must be a JsonSchema[T]
in the implicit scope, which means that there must be a JsonSchema[T]
(or something implicitly convertible to a JsonSchema[T]
) passed through as an implicit argument, as you had with:
case class MetaHolder[T](v: T)(implicit val meta: JsonSchema[T])
来源:https://stackoverflow.com/questions/59391232/when-doing-implicit-resolution-with-type-parameters-why-does-val-placement-matt