How to dynamically generate a type application with scala quasiquotes

142 views Asked by At

I want to generate a type application so I can call a function like foo.bar[com.a.b.SomeType](baz) where com.a.b.SomeType could be any of a broad range of types. I used reflection within the macro runtime to get a reference to actual class represented by SomeType so I can do things like get the package name, the simple name and the fully-qualified name.

When I write tq"com.a.b.SomeType" I get the desired results, and can use interpolation in my expression, e.g.

val someType = tq"com.a.b.SomeType"
q"""
  foo.bar[$someType](baz)
"""

I need to dynamically create that tq expression using class information that I can obtain from within the macro runtime from a string. I looked at the tree generated by tq"com.example.SomeType" and there is a series of nested Select nodes for each of the packages com, a, b, c, which seems cumbersome to generate manually.

Select(Select(Select(Ident(TermName("com")), TermName("a")), TermName("b")), TypeName("SomeType"))

I imagine there is an easier way that I'm just not seeing.

To that end I tried things like:

tq"${someType.getPackage.getName}.${someType.getSimpleName}"

but I can see that isn't right and get errors like:

Compilation error[MyMacro.this.c.universe.Name expected but MyMacro.this.c.universe.Tree found]

So what is a concise way to achieve what I'm trying to do where the type name is only available via reflection, e.g. as an instance of Class?

1

There are 1 answers

1
Mateusz Kubuszok On

You cannot get a type from runtime reflection because this type will be used in generated code and land in binary. However, you can get the type in compile time from the call site using TypeTag or WeakTypeTag

def myMethod[T] = macro macroImplementation

def macroImplementation[T: c.WeakTypeTag](c: Context) = {
  val typeT = weakTypeOf[T].tpe
  val body = q"""
    foo.bar[$typeT](baz)
  """
  ...
}

that should let you create macros for methods with a type parameters, that you could safely fill in runtime (this typeT will then make sure not that you are using a known type, but that you use something that comes from a type parameter and not from a thin air).