Я пытался написать макрос, который генерирует теги классов абстрактных типов.
Я обобщил код, представленный в 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 и понятия не имею, как использовать рекурсию с макросами.
Есть ли какая-нибудь библиотека, которая поддерживает это из коробки?