Неявный поиск внутри макроса не удается - PullRequest
4 голосов
/ 16 мая 2019

Рассмотрим черту, которая выполняет «кодирование» произвольных объектов:

trait Encoder[R] {
  def encode(r: R): Array[Byte]
}

Предполагая, что кодирование примитивных типов известно, и пользовательские типы могут быть закодированы путем определения «сериализатора»:

trait Serializer[T] {
  def serialize(r: T): Array[Byte]
}

мы можем реализовать макрос для кодирования класса дел, просто зациклив поля и неявно просматривая сериализаторы типов. Вот фиктивная реализация, которая ищет сериализатор для самого R (в действительности мы ищем сериализаторы для типов полей класса case):

object Encoder {
  implicit def apply[R <: Product]: Encoder[R] = macro applyImpl[R]
  def applyImpl[R: c.WeakTypeTag](c: blackbox.Context): c.Expr[Encoder[R]] = {
    import c.universe._
    c.Expr[Encoder[R]](q"""
      new ${weakTypeOf[Encoder[R]]} {
        override def encode(r: ${weakTypeOf[R]}): Array[Byte] = 
          implicitly[_root_.Serializer[${weakTypeOf[R]}]].serialize(r)
        }
    """)
  }
}

Теперь определите базовый «процессор»:

abstract class BaseProcessor[R: Encoder] {
  def process(r: R): Unit = {
    println(implicitly[Encoder[R]].encode(r).length)
  }
}

И попробуйте использовать это:

case class Record(i: Int)

object Serializers {
  implicit def recordSerializer: Serializer[Record] = 
    (r: Record) => Array.emptyByteArray
}

import Serializers._

class Processor extends BaseProcessor[Record]

Это не скомпилируется с:

// [error] Loader.scala:10:22: could not find implicit value for parameter e: Serializer[Record]
// [error] class Processor extends BaseProcessor[Record]
// [error]                         ^
// [error] one error found

Однако компилируются следующие компоненты:

class Processor extends BaseProcessor[Record]()(Encoder[Record]) // Compiles!
object x { class Processor extends BaseProcessor[Record] }       // Compiles!

Я не могу понять, почему это происходит, похоже, что это как-то связано с определением Processor, являющимся определением верхнего уровня, поскольку, как только я перемещаю его в класс / объект, все работает, как ожидалось. Вот еще один пример, который не компилируется:

object x {
  import Serializers._ // moving the import here also makes it NOT compile
  class Processor extends BaseProcessor[Record] 
}

Почему это происходит и есть ли способ заставить его работать?

1 Ответ

0 голосов
/ 26 июня 2019

Чтобы это сработало, вы можете попробовать одно из следующих:

1) добавить привязку к контексту

implicit def apply[R <: Product : Serializer]: Encoder[R] = macro applyImpl[R]

def applyImpl[R: c.WeakTypeTag](c: blackbox.Context)(serializer: c.Expr[Serializer[R]]): c.Expr[Encoder[R]] = {
  import c.universe._
  c.Expr[Encoder[R]](q"""
    new Encoder[${weakTypeOf[R]}] {
      override def encode(r: ${weakTypeOf[R]}): Array[Byte] =
        $serializer.serialize(r)
    }
  """)
}

2) сделать макрос whitebox

3) помещает неявный экземпляр recordSerializer: Serializer[Record] в сопутствующий объект Record, а не в какой-либо объект Serializers для импорта

4) возможно, ваш пример проще, чем реальный вариант использования, но теперь кажется, что вам не нужны макросы

implicit def apply[R <: Product : Serializer]: Encoder[R] = new Encoder[R] {
  override def encode(r: R): Array[Byte] = implicitly[Serializer[R]].serialize(r)
}
...