Scala macro that returns a ClassTag of an abstract type and an abstract type-dependent path

I am trying to write a macro that generates abstract type class tags.

I summarized the code provided at StackOverflow question / 2231708 / ... :

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]]]
  }
}

      

Testing with a type class whose abstract type is independent of the path:

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]]
  }
}

      

The above compiled code and test are successful. The macro works as expected when the abstract type is path independent.

Now, consider a type class trying to call this macro with a path dependent abstract type:

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]]
  }
}

      

The code doesn't even compile. I get:

[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

      

How can I change the implementation of the macro to support this use case as well?

I think this will require some kind of recursion, but I'm still new to scala macros and I don't know how to use recursion with macros.

Is there a library out there that supports this out of the box?

+3


source to share





All Articles