Can I generate Scala code from a template (of sorts)?
I know how to do this in Racket/Scheme/Lisp, but not in Scala. Is this something Scala macros can do?
I want to have a code template where X
varies. If I had this code template:
def funcX(a: ArgsX): Try[Seq[RowX]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcX(t, a)}
case _ => Failure(new MissingThingException)
}
and tokens Apple
and Orange
, a macro would take my template, replace the X
s, and produce:
def funcApple(a: ArgsApple): Try[Seq[RowApple]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcApple(t, a)}
case _ => Failure(new MissingThingException)
}
def funcOrange(a: ArgsOrange): Try[Seq[RowOrange]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcOrange(t, a)}
case _ => Failure(new MissingThingException)
}
Try macro annotation with tree transformer
@compileTimeOnly("enable macro paradise")
class generate extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GenerateMacro.impl
}
object GenerateMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val trees = List("Apple", "Orange").map { s =>
val transformer = new Transformer {
override def transform(tree: Tree): Tree = tree match {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" if tname.toString.contains("X") =>
val tname1 = TermName(tname.toString.replace("X", s))
val tparams1 = tparams.map(super.transform(_))
val paramss1 = paramss.map(_.map(super.transform(_)))
val tpt1 = super.transform(tpt)
val expr1 = super.transform(expr)
q"$mods def $tname1[..$tparams1](...$paramss1): $tpt1 = $expr1"
case q"${tname: TermName} " if tname.toString.contains("X") =>
val tname1 = TermName(tname.toString.replace("X", s))
q"$tname1"
case tq"${tpname: TypeName} " if tpname.toString.contains("X") =>
val tpname1 = TypeName(tpname.toString.replace("X", s))
tq"$tpname1"
case q"$expr.$tname " if tname.toString.contains("X") =>
val expr1 = super.transform(expr)
val tname1 = TermName(tname.toString.replace("X", s))
q"$expr1.$tname1"
case tq"$ref.$tpname " if tpname.toString.contains("X") =>
val ref1 = super.transform(ref)
val tpname1 = TypeName(tpname.toString.replace("X", s))
tq"$ref1.$tpname1"
case t => super.transform(t)
}
}
transformer.transform(annottees.head)
}
q"..$trees"
}
}
@generate
def funcX(a: ArgsX): Try[Seq[RowX]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.funcX(t, a)}
case _ => Failure(new MissingThingException)
}
//Warning:scalac: {
// def funcApple(a: ArgsApple): Try[Seq[RowApple]] = w.getThing() match {
// case Some((t @ (_: Thing))) => w.wrap(t)(Detail.funcApple(t, a))
// case _ => Failure(new MissingThingException())
// };
// def funcOrange(a: ArgsOrange): Try[Seq[RowOrange]] = w.getThing() match {
// case Some((t @ (_: Thing))) => w.wrap(t)(Detail.funcOrange(t, a))
// case _ => Failure(new MissingThingException())
// };
// ()
//}
Also you can try approach with type class
def func[A <: Args](a: A)(implicit ar: ArgsRows[A]): Try[Seq[ar.R]] =
w.getThing() match {
case Some(t: Thing) => w.wrap(t){Detail.func(t, a)}
case _ => Failure(new MissingThingException)
}
trait ArgsRows[A <: Args] {
type R <: Row
}
object ArgsRows {
type Aux[A <: Args, R0 <: Row] = ArgsRows[A] { type R = R0 }
implicit val apple: Aux[ArgsApple, RowApple] = null
implicit val orange: Aux[ArgsOrange, RowOrange] = null
}
sealed trait Args
trait ArgsApple extends Args
trait ArgsOrange extends Args
trait Thing
sealed trait Row
trait RowApple extends Row
trait RowOrange extends Row
object Detail {
def func[A <: Args](t: Thing, a: A)(implicit ar: ArgsRows[A]): ar.R = ???
}
class MissingThingException extends Throwable
trait W {
def wrap[R <: Row](t: Thing)(r: R): Try[Seq[R]] = ???
def getThing(): Option[Thing] = ???
}
val w: W = ???
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With