Generate companion object for case class with methods (field = method) - scala

Generate companion object for case class with scala-macros
some code example that i tried, it works i can get list of tuple (name -> type) but how to generate the object in the same scope?
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
} .get
.paramLists
.head
val extractParams = fields.map { field =>
val name = field.asTerm.name
val fieldName = name.decodedName.toString
val NullaryMethodType(fieldType) = tpe.decl(name).typeSignature
c.Expr[List[(String, String)]](
q"""
($fieldName, ${fieldType.toString})
"""
)
Is it possible to annotate some case class and the generated companion make visible in the same scope?
// test case: defined a class with some fields
#GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
goal:
SomeCaseClass.i() // returns Int
SomeCaseClass.b() // returns Byte
SomeCaseClass.c() // returns Char

Your code seems to be intended for def macros. But if you want to generate companion you should use macro annotations
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 Macros._
object App {
#GenerateCompanionWithFields
case class SomeCaseClass(i: Int, b: Byte, c: Char)
}
//Warning:scalac: {
// case class SomeCaseClass extends scala.Product with scala.Serializable {
// <caseaccessor> <paramaccessor> val i: Int = _;
// <caseaccessor> <paramaccessor> val b: Byte = _;
// <caseaccessor> <paramaccessor> val c: Char = _;
// def <init>(i: Int, b: Byte, c: Char) = {
// super.<init>();
// ()
// }
// };
// object SomeCaseClass extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def i(): String = "Int";
// def b(): String = "Byte";
// def c(): String = "Char"
// };
// ()
//}
automatically generate case object for case class

Related

Run Macro to generate code from parent class in Scala

I have a macro that I use to generate some code to call methods dynamically. The macro is more complex than this, but for simplicity let's say it works something like this
def myMacro[T]: Seq[MethodName]
so than when called on
class Hello {
def one(a: Int, b: UserId): String = a.toString + b.id
def two(c: Option[Int]): String = ""
def three(d: Seq[Int], f: Set[Int]): String = ""
}
println(myMacro[Hello]) // Seq("one", "two", "three")
I need this macro to generate code for an internal framework we use at Candu, but I need to be able to call it from the parent's class. So what I want to achieve is:
trait Superclass {
def aFakeMethod: String = ""
val methods = myMacro[Self] // FIXME! self is not defined here...
}
class Hello extends Superclass {
def one(a: Int, b: UserId): String = a.toString + b.id
def two(c: Option[Int]): String = ""
def three(d: Seq[Int], f: Set[Int]): String = ""
}
val hi = new Hello
println(hi.methods) // Seq("one", "two", "three")
Because the high number of classes in the framework, modifying the api between Hello and Superclass is very expansive. So I would need a way to do this without changing code in Hello
Any suggestions on how this could be achieved?
If myMacro worked outside Hello it should work inside Superclass as well
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def myMacro[T]: Seq[String] = macro impl[T]
def impl[T: c.WeakTypeTag](c: blackbox.Context): c.Tree = {
import c.universe._
val methodNames = weakTypeOf[T].decls
.filter(symb => symb.isMethod && !symb.isConstructor)
.map(_.name.toString).toList
val methodNamesTree = methodNames.foldRight[Tree](q"Nil")((name, names) => q"$name :: $names")
q"..$methodNamesTree"
}
Usage:
sealed trait Superclass {
def aFakeMethod: String = ""
val methods = myMacro[Hello]
}
val hi = new Hello
println(hi.methods) // List("one", "two", "three")
If for some reason you can't use the name of Hello you can try to make Superclass sealed and use knownDirectSubclasses
def myMacro1(): Seq[String] = macro impl1
def impl1(c: blackbox.Context)(): c.Tree = {
import c.universe._
val child = c.enclosingClass.symbol.asClass.knownDirectSubclasses.head
q"myMacro[$child]"
}
Usage:
sealed trait Superclass {
def aFakeMethod: String = ""
val methods = myMacro1()
}
val hi = new Hello
println(hi.methods) // List("one", "two", "three")
Or you can replace deprecated c.enclosingClass.symbol.asClass with c.internal.enclosingOwner.owner.asClass (now enclosingOwner is val methods, enclosingOwner.owner is trait Superclass).
If you can't make Superclass sealed try to traverse all classes and look for those extending Superclass
def myMacro2(): Seq[Seq[String]] = macro impl2
def impl2(c: blackbox.Context)(): c.Tree = {
import c.universe._
def treeSymbol(tree: Tree): Symbol = c.typecheck(tree, mode = c.TYPEmode).symbol
val enclosingClassSymbol = c.internal.enclosingOwner.owner
def isEnclosingClass(tree: Tree): Boolean = treeSymbol(tree) == enclosingClassSymbol
var methodss = Seq[Seq[String]]()
val traverser = new Traverser {
override def traverse(tree: Tree): Unit = {
tree match {
case q"$_ class $_[..$_] $_(...$_) extends { ..$_ } with ..$parents { $_ => ..$stats }"
if parents.exists(isEnclosingClass(_)) =>
val methods = stats.collect {
case q"$_ def $tname[..$_](...$_): $_ = $_" => tname.toString
}
methodss :+= methods
case _ => ()
}
super.traverse(tree)
}
}
c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))
def namesToTree[A: Liftable](names: Seq[A]): Tree =
names.foldRight[Tree](q"Seq()")((name, names) => q"$name +: $names")
namesToTree[Tree](methodss.map(namesToTree[String](_)))
}
Usage:
trait Superclass {
def aFakeMethod: String = ""
val methods = myMacro2()
}
class Hello1 extends Superclass {
def four = ???
def five = ???
}
class Hello extends Superclass {
def one(a: Int, b: UserId): String = a.toString + b.id
def two(c: Option[Int]): String = ""
def three(d: Seq[Int], f: Set[Int]): String = ""
}
val hi = new Hello
println(hi.methods) // List(List("four", "five"), List("one", "two", "three"))

how to efficiently/cleanly override a copy method

I have a super and a subclass as follows:
class Animal(var x: Int) {
def greeting: String = "hi im an animal"
def copy: Animal = new Animal(x)
}
class Lion(override var x: Int) extends Animal(x){
override def greeting: String = "hi im a lion"
override def copy: Lion = new Lion(x)
}
I want both of them to have the exact same copy function (imagine it being larger than what I've given), except for the return type, I would like the Lion class to return a Lion when copy is invoked.
How can I cleanly override the Animal copy method without having code duplication?
In principle, methods apply/unapply, canEqual/equals/hashCode, toString, copy, productArity/productElement/productIterator/productPrefix can be generated with Shapeless case classes a la carte although I'm not sure whether this works with class hierarchies.
Anyway, you can generate apply with a macro annotation
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro annotations")
class copy extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro CopyMacro.impl
}
object CopyMacro {
def impl(c: blackbox.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 paramNamess = paramss.map(_.map {
case q"$_ val $tname: $_ = $_" => tname
case q"$_ var $tname: $_ = $_" => tname
})
val tparamNames = tparams.map {
case q"$_ type $tpname[..$_] = $_" => tpname
}
val doesOverrideCopy = parents.map {
case q"${parent#tq"$_[..$_]"}(...$_)" => parent
case parent#tq"$_[..$_]" => parent
}.exists(tree =>
c.typecheck(tree.duplicate, mode = c.TYPEmode)
.tpe
.member(TermName("copy")) != NoSymbol
)
val copyMod = if (doesOverrideCopy) Modifiers(Flag.OVERRIDE) else NoMods
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
$copyMod def copy: $tpname[..$tparamNames] = new $tpname[..$tparamNames](...$paramNamess)
}
..$tail
"""
}
}
}
Usage:
#copy
class Animal(val x: Int) {
def greeting: String = "hi im an animal"
}
#copy
class Lion(override val x: Int) extends Animal(x) {
override def greeting: String = "hi im a lion"
}
//scalac: {
// class Animal extends scala.AnyRef {
// <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// def greeting: String = "hi im an animal";
// def copy: Animal = new Animal(x)
// };
// ()
//}
//scalac: {
// class Lion extends Animal(x) {
// override <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override def greeting: String = "hi im a lion";
// override def copy: Lion = new Lion(x)
// };
// ()
//}
Alternatively, since class Animal(val x: Int) is case-class-like you can try to use shapeless.Generic
implicit class CopyOps[A](a: A)(implicit generic: Generic[A]) {
def copy: A = generic.from(generic.to(a))
}

Pass implicit parameter through multiple objects

I wonder is it possible to pass implicit params through singletons like that
case class Greet(g: String)
object Foo {
def greet(name: String)(implicit greet: Greet = Greet("Hello")) = println(greet.g + " " + name)
}
object Bar {
def greetBar = Foo.greet("Bar")
}
object Main {
def main(args: Array[String]): Unit = {
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Hello Bar
}
}
Bar.greetBar doesn't affected by implicit value in main, but I want it to be affected without passing implicit param to greetBar, so is there any way to do something like that? Maybe there is a way to set an implicit for object but in outer of it?
You should add implicit parameter to the method
object Bar {
def greetBar(implicit greet: Greet /*= Greet("Hello")*/) = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
Bar.greetBar // Goodbye Bar
or make the object a class and add implicit parameter to the class
class Bar(implicit greet: Greet /*= Greet("Hello")*/) {
def greetBar = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
(new Bar).greetBar // Goodbye Bar
I commented out default value /*= Greet("Hello")*/. If you want greetBar not to compile when there is no implicit in scope then you should keep it commented out. If you want behavior similar to greet (i.e. Greet("Hello") when there is no implicit in scope) then you should uncomment it.
Please notice that you can avoid repeating default value if you define lower-priority implicit in companion object
case class Greet(g: String)
object Greet {
implicit val lowPriorityGreet: Greet = Greet("Hello")
}
object Foo {
def greet(name: String)(implicit greet: Greet) = println(greet.g + " " + name)
}
object Bar {
def greetBar(implicit greet: Greet) = Foo.greet("Bar")
}
// class Bar(implicit greet: Greet) {
// def greetBar = Foo.greet("Bar")
// }
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
// (new Bar).greetBar // Goodbye Bar
See also
How to wrap a method having implicits with another method in Scala?
I want to do this to set Greet implict for all methods in Bar
In principle, you can do this with a macro annotation (but you shouldn't)
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro annotations")
class greetAware extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GreetAwareMacro.impl
}
object GreetAwareMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
val greet = TermName(c.freshName("greet"))
val implicitGreet = q"""implicit val $greet: Greet = Greet("Hello")"""
def isImplicit(param: Tree): Boolean = param match {
case q"$mods val $_: $_ = $_" => mods.hasFlag(Flag.IMPLICIT)
}
annottees match {
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil =>
val body1 = body.map {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" =>
val paramss1 =
if (paramss.nonEmpty && paramss.last.nonEmpty && isImplicit(paramss.last.head))
paramss.init :+ (paramss.last :+ implicitGreet)
else paramss :+ List(implicitGreet)
q"$mods def $tname[..$tparams](...$paramss1): $tpt = $expr"
case notMethod => notMethod
}
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body1 }"
}
}
}
Usage:
#greetAware
object Foo {
def greet(name: String) = println(implicitly[Greet].g + " " + name)
}
#greetAware
object Bar {
def greetBar = Foo.greet("Bar")
def xxx(i: Int) = ???
def yyy(i: Int)(implicit s: String) = ???
}
implicit val greet: Greet = Greet("Goodbye")
Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
//scalac: object Foo extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def greet(name: String)(implicit greet$macro$1: Greet = Greet("Hello")) = println(implicitly[Greet].g.$plus(" ").$plus(name))
//}
//scalac: object Bar extends scala.AnyRef {
// def <init>() = {
// super.<init>();
// ()
// };
// def greetBar(implicit greet$macro$2: Greet = Greet("Hello")) = Foo.greet("Bar");
// def xxx(i: Int)(implicit greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark;
// def yyy(i: Int)(implicit s: String, greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark
//}

How to prevent typecheck in StaticAnnotation?

I am creating some macro libraries that reads some information from annotation on the enclosing method.
#info(foo(bar, baz))
def enclosing() = {
myMacro()
}
These information are encoded as foo(bar, baz) in a StaticAnnotation #info.
foo(bar, baz) contains information myMacro need, however, foo(bar, baz) is not able to type-check at the position #info, and cause compiler error when type-checking foo(bar, baz).
I wonder if I can create a macro dontTypecheck that prevent foo(bar, baz) being type checked. So that I can create something like:
#info(dontTypecheck {
foo(bar, baz)
})
def enclosing() = {
myMacro()
}
The dontTypecheck macro should produce a Tree that contains untype-checked foo(bar, baz).
How to create the dontTypecheck macro?
one idea is use another annotation save info
class Info[T](t: T) extends scala.annotation.StaticAnnotation {
}
class AnnInfo extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnInfImpl.impl
}
trait AnnotationUtils {
val c: scala.reflect.macros.blackbox.Context
import c.universe._
final def getAnnotation(x: MemberDef) = x.mods.annotations
}
class AnnInfImpl(val c: blackbox.Context) extends AnnotationUtils {
import c.universe._
// edit 1
def impl(annottees: Tree*): Tree = {
annottees.head match {
case x: DefDef =>
// collect value from `#Info(value)`
val info: List[Tree] = getAnnotation(x).collect { case q"new $name ($value)" => value }
val newBody =
q"""
{
val info = ${info.map(e => show(e))}
println(info)// just print it
${x.rhs}
}"""
DefDef(
mods = Modifiers(), //dropMods
name = x.name,
tparams = x.tparams,
vparamss = x.vparamss,
tpt = x.tpt,
rhs = newBody
)
}
}
}
// test
class AnnInfoTest {
val a = 1
val b = 2
def f(a: Int, b: Int) = a + b
#Info(f(a, b))
#AnnInfo
def e = ???
}
if you call e will print List(f(a, b))

type parameter mismatch with WeakTypeTag reflection + quasiquoting (I think!)

Inspired by travisbrown, I'm trying to use a macro to create some "smart constructors".
Given
package mypkg
sealed trait Hello[A]
case class Ohayo[A,B](a: (A,B)) extends Hello[A]
and
val smartConstructors = FreeMacros.liftConstructors[Hello]
The macro should find all the subclasses of Hello, look at their constructors, and extract a few elements to populate this tree for the "smart constructor":
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
I hoped to get:
val smartConstructors = new {
def ohayo[A, B](a: (A, B)): Hello[A] = Ohayo[A, B](a)
}
but instead get:
error: type mismatch;
found : (A(in class Ohayo), B(in class Ohayo))
required: ((some other)A(in class Ohayo), (some other)B(in class Ohayo))
val liftedConstructors = FreeMacros.liftConstructors[Hello]
At a glance, the tree looks ok to me:
scala> q" new { ..$wellTyped }"
res1: u.Tree =
{
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def ohayo[A, B](a: (A, B)): net.arya.constructors.Hello[A] = Ohayo[A, B](a)
};
new $anon()
}
but I guess it invisibly isn't. If I naively try to freshen up the typeParams with info.typeParams.map(p => TypeName(p.name.toString)), I get "can't splice A as type parameter" when I do the quasiquoting.
Where am I going wrong? Thanks for taking a look.
-Arya
import scala.language.experimental.macros
import scala.reflect.api.Universe
import scala.reflect.macros.whitebox
class FreeMacros(val c: whitebox.Context) {
import c.universe._
import FreeMacros._
def liftedImpl[F[_]](implicit t: c.WeakTypeTag[F[_]]): Tree = {
val atc = t.tpe
val childSymbols: Set[ClassSymbol] = subCaseClassSymbols(c.universe)(atc.typeSymbol.asClass)
val wellTyped = childSymbols.map(ctorsForSymbol(c.universe)(atc)).unzip
q"new { ..${wellTyped} }"
}
}
object FreeMacros {
def liftConstructors[F[_]]: Any = macro FreeMacros.liftedImpl[F]
def smartName(name: String): String = (
name.toList match {
case h :: t => h.toLower :: t
case Nil => Nil
}
).mkString
def subCaseClassSymbols(u: Universe)(root: u.ClassSymbol): Set[u.ClassSymbol] = {
val subclasses = root.knownDirectSubclasses
val cast = subclasses.map(_.asInstanceOf[u.ClassSymbol])
val partitioned = mapped.partition(_.isCaseClass)
partitioned match {
case (caseClasses, regularClasses) => caseClasses ++ regularClasses.flatMap(r => subCaseClassSymbols(u)(r))
}
}
def ctorsForSymbol(u: Universe)(atc: u.Type)(caseClass: u.ClassSymbol): (u.DefDef, u.DefDef) = {
import u._
import internal._
// these didn't help
// def clearTypeSymbol(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, s.pos, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeSymbol2(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeDef(d: TypeDef): TypeDef = internal.typeDef(clearTypeSymbol(d.symbol))
val companionSymbol: Symbol = caseClass.companion
val info: Type = caseClass.info
val primaryCtor: Symbol = caseClass.primaryConstructor
val method = primaryCtor.asMethod
val typeParams = info.typeParams.map(internal.typeDef(_))
// val typeParams = info.typeParams.map(s => typeDef(newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, NoFlags)))
// val typeParams = info.typeParams.map(s => internal.typeDef(clearTypeSymbol2(s)))
val typeArgs = info.typeParams.map(_.name)
val paramLists = method.paramLists.map(_.map(internal.valDef(_)))
val argLists = method.paramLists.map(_.map(_.asTerm.name))
val baseType = info.baseType(atc.typeSymbol)
val List(returnType) = baseType.typeArgs
val methodName = TermName(smartName(caseClass.name.toString))
val wellTyped =
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
wellTyped
}
}
P.S. I have been experimenting with toolbox.untypecheck / typecheck per this article but haven't found a working combination.
you need using
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM),TypeName(name), List(),TypeBoundsTree(EmptyTree, EmptyTree))
}
replace
info.typeParams.map(p => TypeName(p.name.toString))
it si my code
object GetSealedSubClass {
def ol3[T]: Any = macro GetSealedSubClassImpl.ol3[T]
}
class GetSealedSubClassImpl(val c: Context) {
import c.universe._
def showInfo(s: String) =
c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)
def ol3[T: c.WeakTypeTag]: c.universe.Tree = {
//get all sub class
val subClass = c.weakTypeOf[T]
.typeSymbol.asClass.knownDirectSubclasses
.map(e => e.asClass.toType)
//check type params must ia s sealed class
if (subClass.size < 1)
c.abort(c.enclosingPosition, s"${c.weakTypeOf[T]} is not a sealed class")
// get sub class constructor params
val subConstructorParams = subClass.map { e =>
//get constructor
e.members.filter(_.isConstructor)
//if the class has many Constructor then you need filter the main Constructor
.head.map(s => s.asMethod)
//get function param list
}.map(_.asMethod.paramLists.head)
.map(_.map(e => q"""${e.name.toTermName}:${e.info} """))
val outfunc = subClass zip subConstructorParams map {
case (clas, parm) =>
q"def smartConstructors[..${
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
})
}](..${parm})=${clas.typeSymbol.name.toTermName} (..${parm})"
}
val outClass =
q"""
object Term{
..${outfunc}
}
"""
showInfo(show(outClass))
q"""{
$outClass
Term
}
"""
}
}
using like this
sealed trait Hello[A]
case class Ohayo[A, B](a: (A, B)) extends Hello[A]
object GetSealed extends App {
val a = GetSealedSubClass.ol3[Hello[_]]
val b=a.asInstanceOf[ {def smartConstructors[A, B](a: (A, B)): Ohayo[A, B]}].smartConstructors(1, 2).a
println(b)
}