使用宏使 Scala class 扩展 trait/abstract class
Make Scala class extends trait/abstract class with macros
问题:
我想使用 scala 宏使带注释的 class 成为另一个 class 的子 class
我有什么:
字段包装器:
class Field(fieldType: DbModelFieldType, fieldName: String)
一个摘要class(所有注释classes 的基础class):
abstract class DatabaseModel {
def fields: Seq[Fields]
}
我有一个案例class:
Model(num: Int, sym: Char, descr: String)
如果用 @GetFromDB
注释 class
@GetFromDB
Model(num: Int, sym: Char, descr: String)
case class Model(num: Int, sym: Char, descr: String) extends DatabaseModel {
override def fields: Seq[Fields] =
Seq(Field(IntFieldType(), "num"),
Field(CharFieldType(), "sym"),
Field(StringFieldType(), "descr")
)
}
我想要的结果应该是这样的:
val m: DatabaseModel = Model(1, 'A', "First Name")
我看过类似的问题
那么我如何扩展该解决方案以获得预期的结果?
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GenerateCompanionWithFields extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Macro.impl
}
object Macro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
val newMethods = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" =>
q"def $tname(): String = ${tpt.toString}"
}
q"""
$cls
object ${tpname.toTermName} {
..$newMethods
}
"""
}
}
}
}
尝试
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GetFromDB extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GetFromDBMacro.impl
}
object GetFromDBMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
val fields = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" => q"Field(${TermName(tpt.toString + "FieldType")}.apply(), ${tname.toString})"
}
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..${tq"DatabaseModel" :: parents} { $self =>
override def fields: _root_.scala.collection.immutable.Seq[Field] = _root_.scala.collection.immutable.Seq.apply(..$fields)
..$stats
}
..$tail
"""
}
}
}
}
import Macros._
object App {
sealed trait DbModelFieldType
case class IntFieldType() extends DbModelFieldType
case class CharFieldType() extends DbModelFieldType
case class StringFieldType() extends DbModelFieldType
case class Field(fieldType: DbModelFieldType, fieldName: String)
abstract class DatabaseModel {
def fields: Seq[Field]
}
@GetFromDB
case class Model(num: Int, sym: Char, descr: String)
//Warning:scalac: {
// case class Model extends DatabaseModel with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val num: Int = _;
// <caseaccessor> <paramaccessor> val sym: Char = _;
// <caseaccessor> <paramaccessor> val descr: String = _;
// def <init>(num: Int, sym: Char, descr: String) = {
// super.<init>();
// ()
// };
// override def fields: _root_.scala.collection.immutable.Seq[Field] = _root_.scala.collection.immutable.Seq.apply(Field(IntFieldType.apply(), "num"), Field(CharFieldType.apply(), "sym"), Field(StringFieldType.apply(), "descr"))
// };
// ()
//}
val m: DatabaseModel = Model(1, 'A', "First Name")
def main(args: Array[String]): Unit = {
println(
m.fields //List(Field(IntFieldType(),num), Field(CharFieldType(),sym), Field(StringFieldType(),descr))
)
}
}
问题:
我想使用 scala 宏使带注释的 class 成为另一个 class 的子 class 我有什么:
字段包装器:
class Field(fieldType: DbModelFieldType, fieldName: String)
一个摘要class(所有注释classes 的基础class):
abstract class DatabaseModel {
def fields: Seq[Fields]
}
我有一个案例class:
Model(num: Int, sym: Char, descr: String)
如果用 @GetFromDB
@GetFromDB
Model(num: Int, sym: Char, descr: String)
case class Model(num: Int, sym: Char, descr: String) extends DatabaseModel {
override def fields: Seq[Fields] =
Seq(Field(IntFieldType(), "num"),
Field(CharFieldType(), "sym"),
Field(StringFieldType(), "descr")
)
}
我想要的结果应该是这样的:
val m: DatabaseModel = Model(1, 'A', "First Name")
我看过类似的问题
那么我如何扩展该解决方案以获得预期的结果?
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GenerateCompanionWithFields extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro Macro.impl
}
object Macro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case (cls @ q"$_ class $tpname[..$_] $_(...$paramss) extends { ..$_ } with ..$_ { $_ => ..$_ }") :: Nil =>
val newMethods = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" =>
q"def $tname(): String = ${tpt.toString}"
}
q"""
$cls
object ${tpname.toTermName} {
..$newMethods
}
"""
}
}
}
}
尝试
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise")
class GetFromDB extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GetFromDBMacro.impl
}
object GetFromDBMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
val fields = paramss.flatten.map {
case q"$_ val $tname: $tpt = $_" => q"Field(${TermName(tpt.toString + "FieldType")}.apply(), ${tname.toString})"
}
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..${tq"DatabaseModel" :: parents} { $self =>
override def fields: _root_.scala.collection.immutable.Seq[Field] = _root_.scala.collection.immutable.Seq.apply(..$fields)
..$stats
}
..$tail
"""
}
}
}
}
import Macros._
object App {
sealed trait DbModelFieldType
case class IntFieldType() extends DbModelFieldType
case class CharFieldType() extends DbModelFieldType
case class StringFieldType() extends DbModelFieldType
case class Field(fieldType: DbModelFieldType, fieldName: String)
abstract class DatabaseModel {
def fields: Seq[Field]
}
@GetFromDB
case class Model(num: Int, sym: Char, descr: String)
//Warning:scalac: {
// case class Model extends DatabaseModel with scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val num: Int = _;
// <caseaccessor> <paramaccessor> val sym: Char = _;
// <caseaccessor> <paramaccessor> val descr: String = _;
// def <init>(num: Int, sym: Char, descr: String) = {
// super.<init>();
// ()
// };
// override def fields: _root_.scala.collection.immutable.Seq[Field] = _root_.scala.collection.immutable.Seq.apply(Field(IntFieldType.apply(), "num"), Field(CharFieldType.apply(), "sym"), Field(StringFieldType.apply(), "descr"))
// };
// ()
//}
val m: DatabaseModel = Model(1, 'A', "First Name")
def main(args: Array[String]): Unit = {
println(
m.fields //List(Field(IntFieldType(),num), Field(CharFieldType(),sym), Field(StringFieldType(),descr))
)
}
}