Scala 在解析隐式时如何使用显式类型?
How does Scala use explicit types when resolving implicits?
我有以下代码,它使用 spray-json 通过 parseJson
方法将一些 JSON 反序列化为 case class。
根据隐式 JsonFormat[MyCaseClass] 的定义位置(内联或从伴随对象导入),以及定义时是否提供显式类型,代码可能无法编译。
我不明白为什么从伴生对象中导入implicit 需要它在定义时有显式类型,但如果我把它内联,就不是这样了?
有趣的是,IntelliJ 在所有情况下都能正确定位隐式参数(通过 cmd-shift-p)。
我正在使用 Scala 2.11.7。
损坏的代码 - 从伴随对象导入通配符,推断类型:
import SampleApp._
import spray.json._
class SampleApp {
import MyJsonProtocol._
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)
}
}
结果:
Cannot find JsonReader or JsonFormat type class for SampleAppObject.MyCaseClass
请注意,显式导入 myCaseClassSchemaFormat
隐式也会发生同样的事情。
工作代码 #1 - 从伴随对象导入通配符,显式类型:
向伴随对象中的 JsonFormat 添加显式类型会导致代码编译:
import SampleApp._
import spray.json._
class SampleApp {
import MyJsonProtocol._
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
object MyJsonProtocol extends DefaultJsonProtocol {
//Explicit type added here now
implicit val myCaseClassSchemaFormat: JsonFormat[MyCaseClass] = jsonFormat1(MyCaseClass)
}
}
工作代码 #2 - 隐式内联,推断类型:
然而,将隐式参数放在使用它们的地方,没有显式类型,也可以!
import SampleApp._
import spray.json._
class SampleApp {
import DefaultJsonProtocol._
//Now in-line custom JsonFormat rather than imported
implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
}
在搜索 Huw 在他的评论中提到的错误消息后,我找到了这个 2010 年的 Whosebug 问题:Why does this explicit call of a Scala method allow it to be implicitly resolved?
这让我想到了 2008 年创建并于 2011 年关闭的 Scala 问题:https://issues.scala-lang.org/browse/SI-801 ('require explicit result type for implicit conversions?')
马丁表示:
I have implemented a slightly more permissive rule: An implicit conversion without explicit result type is visible only in the text following its own definition. That way, we avoid the cyclic reference errors. I close for now, to see how this works. If we still have issues we migth come back to this.
这成立 - 如果我重新排序中断代码以便首先声明伴生对象,然后代码编译。 (还是有点奇怪!)
(我怀疑我没有看到 'implicit method is not applicable here' 消息,因为我有一个隐式值而不是转换 - 虽然我在这里假设根原因同上)。
我有以下代码,它使用 spray-json 通过 parseJson
方法将一些 JSON 反序列化为 case class。
根据隐式 JsonFormat[MyCaseClass] 的定义位置(内联或从伴随对象导入),以及定义时是否提供显式类型,代码可能无法编译。
我不明白为什么从伴生对象中导入implicit 需要它在定义时有显式类型,但如果我把它内联,就不是这样了?
有趣的是,IntelliJ 在所有情况下都能正确定位隐式参数(通过 cmd-shift-p)。
我正在使用 Scala 2.11.7。
损坏的代码 - 从伴随对象导入通配符,推断类型:
import SampleApp._
import spray.json._
class SampleApp {
import MyJsonProtocol._
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
object MyJsonProtocol extends DefaultJsonProtocol {
implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)
}
}
结果:
Cannot find JsonReader or JsonFormat type class for SampleAppObject.MyCaseClass
请注意,显式导入 myCaseClassSchemaFormat
隐式也会发生同样的事情。
工作代码 #1 - 从伴随对象导入通配符,显式类型:
向伴随对象中的 JsonFormat 添加显式类型会导致代码编译:
import SampleApp._
import spray.json._
class SampleApp {
import MyJsonProtocol._
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
object MyJsonProtocol extends DefaultJsonProtocol {
//Explicit type added here now
implicit val myCaseClassSchemaFormat: JsonFormat[MyCaseClass] = jsonFormat1(MyCaseClass)
}
}
工作代码 #2 - 隐式内联,推断类型:
然而,将隐式参数放在使用它们的地方,没有显式类型,也可以!
import SampleApp._
import spray.json._
class SampleApp {
import DefaultJsonProtocol._
//Now in-line custom JsonFormat rather than imported
implicit val myCaseClassSchemaFormat = jsonFormat1(MyCaseClass)
val inputJson = """{"children":["a", "b", "c"]}"""
println(s"Deserialise: ${inputJson.parseJson.convertTo[MyCaseClass]}")
}
object SampleApp {
case class MyCaseClass(children: List[String])
}
在搜索 Huw 在他的评论中提到的错误消息后,我找到了这个 2010 年的 Whosebug 问题:Why does this explicit call of a Scala method allow it to be implicitly resolved?
这让我想到了 2008 年创建并于 2011 年关闭的 Scala 问题:https://issues.scala-lang.org/browse/SI-801 ('require explicit result type for implicit conversions?')
马丁表示:
I have implemented a slightly more permissive rule: An implicit conversion without explicit result type is visible only in the text following its own definition. That way, we avoid the cyclic reference errors. I close for now, to see how this works. If we still have issues we migth come back to this.
这成立 - 如果我重新排序中断代码以便首先声明伴生对象,然后代码编译。 (还是有点奇怪!)
(我怀疑我没有看到 'implicit method is not applicable here' 消息,因为我有一个隐式值而不是转换 - 虽然我在这里假设根原因同上)。