我有两个声明如下的变量
"firebase": "4.6.2",
"firebase-admin": "^7.4.0",
"firebase-functions": "^2.2.1",
...
"devDependencies": {
"firebase-functions-test": "^0.1.6"
},
两行都编译并运行。
但是,如果我用类型 implicit val decodeURL: Decoder[URL] = Decoder.decodeString.emapTry(s => Try(new URL(s))) // #1
implicit val decodeCompleted = Decoder[List[URL]].prepare(_.downField("completed")) // #2
注释#2。它会编译,并且#2将在运行时抛出implicit val decodeCompleted: Decoder[List[URL]] = Decoder[List[URL]].prepare(_.downField("completed"))
(NPE)。
这怎么可能发生?我不知道这是Circe还是普通的Scala问题。为什么#2与#1不同?谢谢
答案 0 :(得分:3)
问题是您应该始终使用带注释的隐式对象。
现在,当您在未注释时使用它们时,您会陷入未定义/无效行为区域的某种分类。这就是为什么未注释的情况是这样的:
Decoder[List[URL]]
Decoder[List[URL]]
generic.auto._
,因为它的定义在同伴对象中)Decoder[List[URL]]
,因此推断出的类型为decodeCompleted
现在,如果您进行注释会怎样?
Decoder[List[URL]]
decodeCompleted
被声明为符合该定义的东西decodeCompleted
值decodeCompleted
尚未初始化!实际上,我们现在正在初始化它!decodeCompleted = null
这实际上等于:
val decodeCompleted = decodeCompleted
除了间接获取层是通过编译器发现它的荒谬之处之外。 (如果将val
替换为def
,将导致无限递归和堆栈溢出):
@ implicit val s: String = implicitly[String]
s: String = null
@ implicit def s: String = implicitly[String]
defined function s
@ s
java.lang.StackOverflowError
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
ammonite.$sess.cmd1$.s(cmd1.sc:1)
是的,编译器将其弄乱了。您没有做错任何事情,在一个完美的世界中,它会起作用。
Scala社区通过与众不同来缓解这种情况:
在后一种情况下,通常会有一些实用程序,例如:
import io.circe.generic.semiauto._
implicit val decodeCompleted: Decoder[List[URL]] = deriveDecoder[List[URL]]
之所以有效,是因为它隐含了DerivedDecoder[A]
,然后从中提取Decoder[A]
,所以您永远不会遇到implicit val a: A = implicitly[A]
的情况。
答案 1 :(得分:1)
实际上,问题在于您引入了递归"?m=0"
,就像@MateuszKubuszok解释的那样。
最直接(尽管有点难看)的解决方法是:
val
通过在右侧隐藏implicit val decodeCompleted: Decoder[List[URL]] = {
val decodeCompleted = null
Decoder[List[URL]].prepare(_.downField("completed"))
}
,隐式搜索将不再将其视为该代码块内的候选对象,因为无法再对其进行引用。