2015-01-08 2 views
0

У меня возникла проблема с 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, удаляя его, устраняет проблему. Когда xmMaterializer[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.

+0

Я не знаю решения, но иметь два неявных значения, один для типа, а другой для его надтипа, кажется, нарушают правила неявных разрешений , И для меня имеет смысл, что это не работает. Определите имплициты только для конкретных подтипов T, а не только для T. Это должно сработать. –

+0

Пример из Json Format [T], где я хочу, чтобы вызов API возвращал однородный объект. Я хочу автоматически добавить свойство «_type» в JsObject, создаваемый автором Json.format [X]. Для считывания значения мне нужно проверить _type в json и выбрать правильное чтение [X]. Это приведет к тому, что формат [T], который, я думаю, является именно тем, что мне нужно. – Jaap

ответ

1

Я не уверен, что вы подразумеваете под «исправлениями» или «это работает», но это просто разрешение перегрузки на работе.

Когда оба являются 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] {} 
} 
Смежные вопросы