4

I would like to use a macro that uses a type defined by the "type" keyword. How do I reference it?

My starting code is

import scala.reflect.macros.Context
import scala.language.experimental.macros

trait Demo6 {
  type T

  def add(param: Any): T = macro Demo6.addImpl[T]

  def fullAdd(param: Any, toStringBasedOnAST: String): T = {
    doesSomeThing_and_returnsSomething_OfTypeT
  }
  def doesSomeThing_and_returnsSomething_OfTypeT: T //just to allow compilation
}

object Demo6 {
  def addImpl[T: c.WeakTypeTag](c: Context)(param: c.Expr[Any]): c.Expr[T] = {
    import c.universe._
    reify { (c.Expr[Demo6](c.prefix.tree)).splice.fullAdd(param.splice, 
                                        c.literal(show(param.tree)).splice) }
    //        ^ - type mismatch; found : org.autotdd.scalamacros.Demo6#T 
    //                           required: T
  }
}

I have marked up the compiler error in the example. It's pretty clear what is happening: the type T defined by keyword isn't the same as the type T that I am passing in.

Things I've tried There isn't a lot of documentation yet on scala-macros. The section in http://docs.scala-lang.org/overviews/macros/overview.html was very helpful in getting me this far, but its example uses a class level and a method level generic. I've browsed the code for Expecty and macrocosm, which are projects referenced in the documentation, but couldn't find code like this.

Stave Escura
  • 2,058
  • 1
  • 19
  • 24
  • Do you need to use the type tag for `T`? If not, you don't even need the `T` parameter for `addImpl`—you should be able to use `Typed` and `TypeName("T")`. – Travis Brown May 08 '13 at 11:59

1 Answers1

4

Your code is almost correct, just change type parameter of Expr:

val expr = reify { ... }
c.Expr[T](expr.tree)

Without reify you should return this:

c.Expr[T](Apply(Select(c.prefix.tree, newTermName("fullAdd")),
                List(param.tree, Literal(Constant(show(param.tree))))))

reify creates the same, but with wrong type parameter.

See this answer for showRaw usage.

In this case:

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> {
     |   object Demo { def fullAdd(param1: Any, param2: String): String = "result" }
     |   showRaw{ reify { Demo.fullAdd("param1", "param2") } }
     | }
res0: String = Expr(Apply(Select(Ident(newTermName("Demo")), newTermName("fullAdd")), List(Literal(Constant("param1")), Literal(Constant("param2")))))

Replace Ident(newTermName("Demo")) with c.prefix.tree, Literal(Constant("param1")) with param.tree and "param2" with show(param.tree) and you'll get your result.

Community
  • 1
  • 1
senia
  • 37,745
  • 4
  • 88
  • 129
  • 1
    Thank you: really worked well. I'm struggling with understanding how you worked that out. Can you suggest any code to look at that would help me here? – Stave Escura May 08 '13 at 10:50
  • That is a really nice link thank you. I can see me spending a few hours reading it :) – Stave Escura May 08 '13 at 12:46