使用逆变泛型类型时的模糊隐含值

时间:2015-01-08 17:45:12

标签: scala implicit scala-macros

我在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的显式类型声明,删除它,修复问题。当xmMaterializer[X]而不是OMaterializer[X]时,它也有效。

在寻找inferImplicitValue时,

tm同时考虑xmMaterializer[X]。当xm属于Materializer[X]类型且tm具有类型时 Materializer[T]推荐人更喜欢xm超过tm,因为它完全匹配。但是当xmOMaterializer[X]时,编译器无法再决定哪一个 更好,并抛出错误。

正如我所说,从tm中删除显式类型声明可以解决问题,因为在宏执行时只有xm的类型已知。

我可以解决inferImplicitValue有这个问题吗? silent选项已经为true(默认情况下)。

在我的实际用例中,我有多个TXYZ)的实现,然后使用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。

1 个答案:

答案 0 :(得分:1)

我不确定你的意思"修复"或"它可以工作",但这只是工作中的重载解决方案。

当两者都为Materializer时,tmMat[T] <:< Mat[X]而获胜。

如果用例是在你展示的情况下引入隐式tm,那么在隐藏的范围内,但是这是我能想到的唯一技巧:

xm

只需从显式范围中删除隐式 implicit val tm: Materializer[T] = { val tm = 0 Macro.genMaterializer[T, X] } 即可。

也许一个丑陋的宏可以自动生成该块,从中扩展真正的宏。这打破了地方。

通常,您通过使其不明确来消除隐式,但您希望隐藏在此范围内。因此,阴影仅将其从嵌套范围中删除。

这没有用,但很自然:

tm