使用逆变泛型类型时的隐含值不明确
ambiguous implicit values when using contravariant generic type
我在 Scala 宏中遇到了 inferImplicitValue 问题。我在玩 play 的 json 库 Format[T]
的宏。
我可以将其缩小到一个问题,即 Writes[T]
有时是如何用 OWrites[T]
实现的。连同显式类型声明
implicit val
,这导致了以下编译器错误。
[error] ambiguous implicit values:
[error] both value xm in object TestTest of type => OMaterializer[X]
[error] and value tm in object TestTest of type => Materializer[T]
[error] match expected type Materializer[X]
[error] one error found
[error] (root/compile:compile) Compilation failed
我们看代码(SBT项目可以在这里找到,https://github.com/q42jaap/scala-macro-inferImplicitValue)
// models play json lib's Writes
trait Materializer[-T]
// models play json lib's OWrites
trait OMaterializer[T] extends Materializer[T]
trait T
case class X() extends T
object TestTest {
// The OMaterializer trait here is the second part of the problem
implicit val xm = new OMaterializer[X] {}
// the explicit `tm: Materializer[T]` type declaration here is first part of the problem
implicit val tm: Materializer[T] = Macro.genMaterializer[T, X]
}
object Macro {
def genMaterializer[T, M]: Materializer[T] = macro MacroImpl.genMaterializer[T, M]
}
object MacroImpl {
def genMaterializer[T: c.WeakTypeTag, M: c.WeakTypeTag](c: blackbox.Context): c.Expr[Materializer[T]] = {
val tMaterializerTpe = c.universe.appliedType(c.typeOf[Materializer[_]], c.weakTypeOf[M])
c.inferImplicitValue(tMaterializerTpe)
c.universe.reify {
new Materializer[T] {}
}
}
}
请注意 tm
的显式类型声明,将其删除即可解决问题。当 xm
是 Materializer[X]
而不是 OMaterializer[X]
时,它也有效。
inferImplicitValue
在查找 Materializer[X]
时同时考虑 tm
和 xm
。当 xm
的类型为 Materializer[X]
并且 tm
的类型为
Materializer[T]
推断者更喜欢 xm
而不是 tm
,因为它是完全匹配。但是当 xm
是 OMaterializer[X]
时,编译器无法再决定是哪一个
更好并抛出错误。
正如我所说,从 tm
中删除显式类型声明可以解决问题,因为在执行宏时只有 xm
的类型是已知的。
我可以解决这个问题inferImplicitValue
了? silent
选项已经为真(默认情况下)。
在我的实际用例中,我有多个 T
的实现(X
、Y
、Z
),然后使用联合类型(如 reactivemongo做)到宏:
genMaterializer[T, Union[X \/ Y \/ Z]]
所以我必须使用 inferImplicitValue 来找到 X、Y 和 Z 的 Materializer。
请注意,我已将此案例进一步简化为
object ThisDoesntWorkToo {
implicit val xm = new OMaterializer[X] {}
implicit val tm: Materializer[T] = withoutMacro[X]
def withoutMacro[A](implicit m: Materializer[A]): Materializer[A] = m
}
它不使用宏,但有相同的编译器错误:
[error] TestTest.scala:15: ambiguous implicit values:
[error] both value xm in object ThisWorks of type => OMaterializer[X]
[error] and value tm in object ThisWorks of type => Materializer[T]
[error] match expected type Materializer[X]
[error] implicit val tm: Materializer[T] = withoutMacro[X]
这简化了这里的情况,但仍然给我留下了隐式 val 的实现可以引用自身的问题。
在后一种情况下,简单明了的方法是显式提供隐式值,但正如在宏的最终版本中所论证的那样,我需要使用 inferImplicitValue
因为我有一个类型列表,我必须为其找到一个 Materializer。
我不确定你所说的 "fixes" 或 "it works" 是什么意思,但这只是工作中的重载解析。
当两者都是Materializer
时,tm
获胜,因为Mat[T] <:< Mat[X]
。
如果用例是在该范围内引入隐式 tm
,但隐式地选择 xm
,那么这是我能想出的唯一技巧:
implicit val tm: Materializer[T] = {
val tm = 0
Macro.genMaterializer[T, X]
}
通过简单地从显式范围中消除隐式 tm
来工作。
也许一个丑陋的宏可以自动生成那个块,从中展开真正的宏。这打破了局部性。
通常,您通过使隐含不明确来消除隐含,但您希望隐含在此范围内。因此,阴影仅将其从嵌套范围中删除。
这没有帮助,但很自然:
object X {
implicit val xm: OMaterializer[X] = new OMaterializer[X] {}
}
我在 Scala 宏中遇到了 inferImplicitValue 问题。我在玩 play 的 json 库 Format[T]
的宏。
我可以将其缩小到一个问题,即 Writes[T]
有时是如何用 OWrites[T]
实现的。连同显式类型声明
implicit val
,这导致了以下编译器错误。
[error] ambiguous implicit values:
[error] both value xm in object TestTest of type => OMaterializer[X]
[error] and value tm in object TestTest of type => Materializer[T]
[error] match expected type Materializer[X]
[error] one error found
[error] (root/compile:compile) Compilation failed
我们看代码(SBT项目可以在这里找到,https://github.com/q42jaap/scala-macro-inferImplicitValue)
// models play json lib's Writes
trait Materializer[-T]
// models play json lib's OWrites
trait OMaterializer[T] extends Materializer[T]
trait T
case class X() extends T
object TestTest {
// The OMaterializer trait here is the second part of the problem
implicit val xm = new OMaterializer[X] {}
// the explicit `tm: Materializer[T]` type declaration here is first part of the problem
implicit val tm: Materializer[T] = Macro.genMaterializer[T, X]
}
object Macro {
def genMaterializer[T, M]: Materializer[T] = macro MacroImpl.genMaterializer[T, M]
}
object MacroImpl {
def genMaterializer[T: c.WeakTypeTag, M: c.WeakTypeTag](c: blackbox.Context): c.Expr[Materializer[T]] = {
val tMaterializerTpe = c.universe.appliedType(c.typeOf[Materializer[_]], c.weakTypeOf[M])
c.inferImplicitValue(tMaterializerTpe)
c.universe.reify {
new Materializer[T] {}
}
}
}
请注意 tm
的显式类型声明,将其删除即可解决问题。当 xm
是 Materializer[X]
而不是 OMaterializer[X]
时,它也有效。
inferImplicitValue
在查找 Materializer[X]
时同时考虑 tm
和 xm
。当 xm
的类型为 Materializer[X]
并且 tm
的类型为
Materializer[T]
推断者更喜欢 xm
而不是 tm
,因为它是完全匹配。但是当 xm
是 OMaterializer[X]
时,编译器无法再决定是哪一个
更好并抛出错误。
正如我所说,从 tm
中删除显式类型声明可以解决问题,因为在执行宏时只有 xm
的类型是已知的。
我可以解决这个问题inferImplicitValue
了? silent
选项已经为真(默认情况下)。
在我的实际用例中,我有多个 T
的实现(X
、Y
、Z
),然后使用联合类型(如 reactivemongo做)到宏:
genMaterializer[T, Union[X \/ Y \/ Z]]
所以我必须使用 inferImplicitValue 来找到 X、Y 和 Z 的 Materializer。
请注意,我已将此案例进一步简化为
object ThisDoesntWorkToo {
implicit val xm = new OMaterializer[X] {}
implicit val tm: Materializer[T] = withoutMacro[X]
def withoutMacro[A](implicit m: Materializer[A]): Materializer[A] = m
}
它不使用宏,但有相同的编译器错误:
[error] TestTest.scala:15: ambiguous implicit values:
[error] both value xm in object ThisWorks of type => OMaterializer[X]
[error] and value tm in object ThisWorks of type => Materializer[T]
[error] match expected type Materializer[X]
[error] implicit val tm: Materializer[T] = withoutMacro[X]
这简化了这里的情况,但仍然给我留下了隐式 val 的实现可以引用自身的问题。 在后一种情况下,简单明了的方法是显式提供隐式值,但正如在宏的最终版本中所论证的那样,我需要使用 inferImplicitValue 因为我有一个类型列表,我必须为其找到一个 Materializer。
我不确定你所说的 "fixes" 或 "it works" 是什么意思,但这只是工作中的重载解析。
当两者都是Materializer
时,tm
获胜,因为Mat[T] <:< Mat[X]
。
如果用例是在该范围内引入隐式 tm
,但隐式地选择 xm
,那么这是我能想出的唯一技巧:
implicit val tm: Materializer[T] = {
val tm = 0
Macro.genMaterializer[T, X]
}
通过简单地从显式范围中消除隐式 tm
来工作。
也许一个丑陋的宏可以自动生成那个块,从中展开真正的宏。这打破了局部性。
通常,您通过使隐含不明确来消除隐含,但您希望隐含在此范围内。因此,阴影仅将其从嵌套范围中删除。
这没有帮助,但很自然:
object X {
implicit val xm: OMaterializer[X] = new OMaterializer[X] {}
}