У меня возникла проблема с inferImplicitValue в макросе scala. Я играл с макросом для игры json libary Format[T]
. Я мог бы сузить его до проблемы, так как Writes[T]
иногда реализуется с OWrites[T]
. Вместе с явным объявлением типа на a 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
уже верна (по умолчанию).
В моей реальной пользы дела, у меня есть несколько реализаций T
(X
, Y
, Z
) и передать затем с типом союза (как reactivemongo делает) к Macro:
genMaterializer[T, Union[X \/ Y \/ Z]]
Так я должен использовать inferImplicitValue найти Materializer для X, Y и Z.
Следует заметить, что я еще более упростил этот случай
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]
Это упрощает дело здесь, но по-прежнему оставляет меня с проблемой, что реализация неявной Валу может относиться к самому себе. В последнем случае легко и очевидно, что явное предоставление неявного значения, но, как указано в окончательной версии макроса, мне нужно использовать inferImplicitValue , потому что у меня есть список типов, для которых я должен найти Materializer.
Я не знаю решения, но иметь два неявных значения, один для типа, а другой для его надтипа, кажется, нарушают правила неявных разрешений , И для меня имеет смысл, что это не работает. Определите имплициты только для конкретных подтипов T, а не только для T. Это должно сработать. –
Пример из Json Format [T], где я хочу, чтобы вызов API возвращал однородный объект. Я хочу автоматически добавить свойство «_type» в JsObject, создаваемый автором Json.format [X]. Для считывания значения мне нужно проверить _type в json и выбрать правильное чтение [X]. Это приведет к тому, что формат [T], который, я думаю, является именно тем, что мне нужно. – Jaap