我在scala宏中遇到了inferImplicitValue的问题。我正在玩一个用于播放json libary 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
时, tm
同时考虑xm
和Materializer[X]
。当xm
属于Materializer[X]
类型且tm
具有类型时
Materializer[T]
推荐人更喜欢xm
超过tm
,因为它完全匹配。但是当xm
为OMaterializer[X]
时,编译器无法再决定哪一个
更好,并抛出错误。
正如我所说,从tm
中删除显式类型声明可以解决问题,因为在宏执行时只有xm
的类型已知。
我可以解决inferImplicitValue
有这个问题吗? silent
选项已经为true(默认情况下)。
在我的实际用例中,我有多个T
(X
,Y
,Z
)的实现,然后使用union类型传递(就像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。
答案 0 :(得分:1)
我不确定你的意思"修复"或"它可以工作",但这只是工作中的重载解决方案。
当两者都为Materializer
时,tm
因Mat[T] <:< Mat[X]
而获胜。
如果用例是在你展示的情况下引入隐式tm
,那么在隐藏的范围内,但是这是我能想到的唯一技巧:
xm
只需从显式范围中删除隐式 implicit val tm: Materializer[T] = {
val tm = 0
Macro.genMaterializer[T, X]
}
即可。
也许一个丑陋的宏可以自动生成该块,从中扩展真正的宏。这打破了地方。
通常,您通过使其不明确来消除隐式,但您希望隐藏在此范围内。因此,阴影仅将其从嵌套范围中删除。
这没有用,但很自然:
tm