Created
July 10, 2013 16:38
-
-
Save xeno-by/5967900 to your computer and use it in GitHub Desktop.
Macro-powered structural types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.annotation.StaticAnnotation | |
import scala.reflect.macros.Macro | |
import language.experimental.macros | |
class body(tree: Any) extends StaticAnnotation | |
trait Macros extends Macro { | |
import c.universe._ | |
def selFieldImpl = { | |
val field = c.macroApplication.symbol | |
val bodyAnn = field.annotations.filter(_.tpe <:< typeOf[body]).head | |
bodyAnn.scalaArgs.head | |
} | |
def mkObjectImpl(xs: c.Tree*) = { | |
val kvps = xs.toList map { case q"${_}(${Literal(Constant(name: String))}).->[${_}]($value)" => name -> value } | |
val fields = kvps map { case (k, v) => q"@body($v) def ${TermName(k)} = macro Macros.selFieldImpl" } | |
q"class Workaround { ..$fields }; new Workaround{}" | |
} | |
} | |
object mkObject { | |
def apply(xs: Any*) = macro Macros.mkObjectImpl | |
} | |
================= | |
object Test { | |
def main(args: Array[String]) = { | |
val foo = mkObject("x" -> "2", "y" -> 3) | |
println(foo.x) | |
println(foo.y) | |
// println(foo.z) => will result in a compilation error | |
} | |
} |
I've re-implemented it for a newer version of Scala, but when I'm trying to use these types - I see reflMethod$Method1
with Lscala/runtime/StructuralCallSite
and other reflection related instructions. My original impression was that since we are generating proper types - we don't need reflection.
I assume the problem is related to the scope in which macros operates,
is there any way I can generate new type and properly return it from a macros ?
Maybe it would work better as compiler plugin ?
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
object Record {
def apply(xs: => Any): Product = macro applyImpl
def applyImpl(c: Context)(xs: c.Tree): c.Tree = {
import c.universe._
val fieldsInfo = xs match {
case q"{..$fieldsExpr}" =>
fieldsExpr.map(f => f match {
case q"val $name: $valType = $value" =>
if(valType.isEmpty) (name, value.tpe.erasure, value)
else (name, valType.tpe, value)
case q"var $name: $valType = $value" =>
c.abort(
c.enclosingPosition,
s"var fields are not supported")
case _ => c.abort(
c.enclosingPosition,
s"Error in ${f.toString()} in position. Only applicable to assignment expressions"
)
})
case _ => c.abort(
c.enclosingPosition,
"Only applicable to block expression"
)
}
q"""{
case class Workaround(..${fieldsInfo.map(x => q"${x._1}: ${x._2}")});
new Workaround(..${fieldsInfo.map(_._3)});
}"""
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
No reflection involved!