Макрос Scala, который возвращает ClassTag абстрактного типа и абстрактного типа, зависящего от пути

Я пытался написать макрос, который генерирует теги классов абстрактных типов.

Я обобщил код, представленный в https://stackoverflow.com/a/19502658/2859613:

object Macro {
  import scala.reflect.ClassTag
  import scala.reflect.macros.blackbox

  def typeClassTag[A: c.WeakTypeTag]
    (c: blackbox.Context): c.Expr[ClassTag[A]] = {
    import c.universe._

    val A = c.prefix.tree.tpe.member(weakTypeOf[A].typeSymbol.name).typeSignature

    c.Expr(q"implicitly[ClassTag[$A]]").asInstanceOf[c.Expr[ClassTag[A]]]
  }
}

Тестирование с классом типа, абстрактный тип которого не зависит от пути:

import scala.language.experimental.macros
import scala.reflect.ClassTag
import Macro._

trait GatewayService {
  type Request
  type Response

  def requestTag : ClassTag[Request]  = macro typeClassTag[Request]
  def responseTag: ClassTag[Response] = macro typeClassTag[Response]
}

trait TransferRequest
trait TransferResponse

trait TransferService extends GatewayService {
  type Request  = TransferRequest
  type Response = TransferResponse
}

class TransferServiceImpl extends TransferService

"TransferServiceImpl  requestTag and responseTag" should {
  "equal ClassTag[TransferRequest] and ClassTag[TransferResponse] respectively" in {
    val transferService = new TransferServiceImpl

    transferService.requestTag  shouldEqual implicitly[ClassTag[TransferRequest]]
    transferService.responseTag shouldEqual implicitly[ClassTag[TransferResponse]]
  }
}

Приведенный выше код компилируется, и тест проходит успешно. Макрос работает должным образом, когда абстрактный тип не зависит от пути.

Теперь посмотрите на этот класс типов, пытающийся вызвать этот макрос с абстрактным типом, который зависит от пути:

import scala.language.experimental.macros
import scala.reflect.ClassTag
import Macro._

trait GatewayFrontend {
  type Service <: GatewayService  // GatewayService was defined in the code above

  def requestTag : ClassTag[Service#Request]  = macro typeClassTag[Service#Request]
  def responseTag: ClassTag[Service#Response] = macro typeClassTag[Service#Response]
}

trait TransferFrontend extends GatewayFrontend {
  type Service = TransferService  // TransferService was defined in the code above
}

class TransferFrontendImpl extends TransferFrontend

"TransferFrontendImpl requestTag and responseTag" should {
  "equal ClassTag[TransferRequest] and ClassTag[TransferResponse] respectively" in {
    val transferFrontend = new TransferFrontendImpl

    transferFrontend.requestTag  shouldEqual implicitly[ClassTag[TransferRequest]]
    transferFrontend.responseTag shouldEqual implicitly[ClassTag[TransferResponse]]
  }
}

Код даже не компилируется. Я получил:

[error] No ClassTag available for <notype>
[error]       transferFrontend.requestTag  shouldEqual implicitly[ClassTag[TransferRequest]]
[error]                        ^
[error]
[error] No ClassTag available for <notype>
[error]       transferFrontend.responseTag shouldEqual implicitly[ClassTag[TransferResponse]]
[error]                        ^
[error]
[error] two errors found
[error] (macro-utils/test:compileIncremental) Compilation failed

Как я могу изменить реализацию макроса, чтобы он также поддерживал этот вариант использования?

Я думаю, что для этого потребуется какая-то рекурсия, но я все еще новичок в макросах scala и понятия не имею, как использовать рекурсию с макросами.

Есть ли какая-нибудь библиотека, которая поддерживает это из коробки?


person Rafael-ST-RJ    schedule 05.06.2015    source источник