Can I take a class as an argument to a macro annotation - scala

I'm attempting to build a scala class based on some java classes generated by a third party annotation pre-processor.
I'd like to be able to "point" to a class from an annotated object, for example:
#MyAnnotation(classOf[GeneratedJavaClass]) object MyObject
or
#MyAnnotation object MyObject extends PlaceHolderTrait[GeneratedJavaClass]
Once I'm in the actual macro implementation, I would like to reflect on GeneratedJavaClass to find it's members which I'll use to build the implementation of MyObject
My starting point so far is based on https://github.com/travisbrown/type-provider-examples/blob/master/rdfs-public/src/main/scala/public/PrefixGenerator.scala.
I've tried to understand how I could take a Class[T] as the argument to the annotation and then match on c.macroApplication with Apply(Select(Apply(_, List(TypeApply(_, List(catalog)))), _), _) but the type I get is a TypeApply(_, List(Trees$Ident) and I don't see a way to get the class from there (I assume classOf[T] isn't a literal).
As an alternative, I thought I'd try to extract the type I want from a trait that I have the object extend. I tried matching the annotee against case List(q"object $name extends PlaceHolderTrait[$parent] { ..$body }") but again end up with a Trees$Ident and am not sure how to get the class that is being referenced.
I realize I could probably just pass a String of the fully qualified name and use reflection to get the class, but I was hoping for a nicer alternative. Note that I'm not tied to those 2 alternatives for specifying the class, they are just the 2 options I've been able to come up with.

Ok, finally got something working, based on https://github.com/scalamacros/paradise/issues/69 I realized that at the stage I'm running in the typer hasn't run and therefore expecting to be given a type is a bit silly. The macro api does provide the typeCheck method however, which lets you run the typer on a tree, as follows:
class AnnotationArgument[T] extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val macroTypeWithArguments = c.typeCheck(q"${c.prefix.tree}").tpe // my.package.AnnotationArgument[my.package.MyClass]
val annotationClass: ClassSymbol = macroTypeWithArguments.typeSymbol.asClass // my.package.AnnotationArgument
val annotationTypePlaceholder: Type = annotationClass.typeParams.head.asType.toType // T
val argumentType: Type = annotationTypePlaceholder.asSeenFrom(args, annotationClass) // my.package.MyClass
println(s"the argument's type is $argumentType")
q"..${annottees}"
}
}
import my.package.MyClass
#AnnotationArgument[MyClass]
class AnnotationArgumentTestClass

another thought :
use another class annotation save class info
class AnnotationArgumentClass[T](clazz: Class[T]) extends StaticAnnotation
class AnnotationArgument extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro AnnotationArgumentImpl.impl
}
class AnnotationArgumentImpl(val c: blackbox.Context) {
import c.universe._
def impl(annottees: c.Expr[Any]*): c.Tree = {
val classValue = annottees.head.tree match {
case x: MemberDefApi => x.mods.annotations collectFirst {
case q"new $annotation($classValue)" => classValue
}
}
/*edit :*/
val x = (c.eval(c.Expr[Type](c.typecheck(classValue.get))))
println(x.typeSymbol.fullName)
q"..${annottees}"
}
}
test :
package aaa
class AAA
//
import aaa.AAA
#AnnotationArgument
#AnnotationArgumentClass(classOf[AAA])
class AnnotationArgumentTestClass

Related

How to access parent class params literals?

Using Scala 2.x macros, how to access literals passed to parent classes when defining subclasses, typically with ADTs such as the following:
sealed abstract class Base(val s: String, val i: Int)
object Base {
case object A extends Base("a", 1)
case object B extends Base("b", 2)
case class C(override val s: String) extends Base(s, 3)
}
For object A, I would like to know parameter values are literals "a" and 1.
For object B, I would like to know parameter values are literals "b" and 2.
For case class C, I would like to know the second parameter is literal 3.
How can I accomplish this with Scala 2.x macros?
After much investigation and getting inspiration from bwmcadams /
supreme-macro-adventure, it looks like one solution is based on the combination of Macro Paradise and the creation of an #ADT annotation.
Macro paradise provides a mechanism to expand annotations on ADTs to rewrite them, thanks to the macroTransform function.
import scala.annotation.{compileTimeOnly, StaticAnnotation}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
#compileTimeOnly("enable macro paradise to expand macro annotations")
class ADT extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ADT.impl
}
object ADT {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val trees = annottees.map(_.tree)
val rewritten = trees match {
case (cd: ClassDef) :: q"object $name { ..$body }" :: Nil =>
$body match {
case q"case object $name extends $_($literal, $_)" :: tail =>
[...]
}
case _ =>
trees
}
c.Expr[Any](q"{..$rewritten}")
}
}
With the help of quasiquotes, one can navigate $body and pattern match the different cases to access the arguments passed to the parent class (here $literal). Finally, one returns the rewritten ADT.

Scala macro: get companion object from class type

I cannot manage to get the companion object / singleton from a class type in Scala macro / quasiquotes. Tried to follow https://docs.scala-lang.org/overviews/quasiquotes/type-details.html#singleton-type, the given example works but it is based on a literal string to quasiquote to get the companion object directly, which I cannot quite achieve the same thing if I start off from an extracted class type of a param after some quasiquote unlifting.
I have simplified and tried to highlight the intended usage, and current macro implementation below:
// Intended usage
package utils
sealed trait Base
object Base {
import macros._
#Component
final case class X(v: XInner) extends Base
}
final case class XInner(v: Int)
Base.X(123) // No need to do Base.X(XInner(123))
The current macro implementation
package macros
import scala.reflect.macros.whitebox
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
import scala.annotation.compileTimeOnly
class Component() extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ComponentMacro.impl
}
private class ComponentMacro(val c: whitebox.Context) {
import c.universe._
// To map function result while allowing the use of params forwarding syntax like `apply _`
// e.g. `def y = (X.apply _).mapResult(Y(_))`
implicit class Func1Extra[I1, O1, O2](f: I1 => O1) {
def mapResult(g: O1 => O2): I1 => O2 = (i1: I1) => g(f(i1))
}
def impl(annottees: Tree*): Tree = annottees match {
case (clsDef: ClassDef) :: Nil =>
clsDef match {
case q"final case class $className(..$fields) extends ..$parents" if fields.length == 1 => {
val fieldType = fields(0).tpt
val singletonType = tq"$fieldType.type"
val tq"$singleton.type" = singletonType
q"""
$clsDef
object ${clsDef.name.toTermName} {
def apply = (${singleton}.apply _).mapResult(new ${clsDef.name}(_))
}
"""
}
case _ => c.abort(c.enclosingPosition, "Invalid annotation target")
}
case _ => c.abort(c.enclosingPosition, "Invalid annotation target")
}
}
The error while compiling is:
value apply is not a member of Utils.XInner
The error message seems to suggest that the apply method was done on the XInner class type, rather than the companion object of XInner.
Any idea as to how to get the component object of the same type name? Thanks in advance!

Make an implicit from a different object accessible when my object is imported

When someone imports my object, I would like some implicit classes from other objects (or packages) to be available to them. Having import in the object does not help, as imports are not imported transitively, therefore I assume I must use some implicit def or val, however I am unable to find some reasonable way how to achieve this, only a quite verbose def:
object Main extends App {
object A {
implicit class ExtendedMath(val x: Double) extends AnyVal {
def square = x * x
}
}
object X {
import A._
// what to write here, so that ExtendedMath is forwarded to our users?
// following works, but it seems quite verbose
implicit def extendedMath(x: Double): ExtendedMath = ExtendedMath(x)
}
import X._
val a = 0.0
println(a.square)
}
Is there some more concise way?
As #stew suggests, the typical approach is to define the implicits in a trait that you can then mix in multiple times. The only caveat is that value classes are not allowed inside a trait, so you have to resort to separating value class and implicit conversion:
class ExtendedMath(val x: Double) extends AnyVal {
def square = x * x
}
trait HasMath {
// note: method name somehow must not shadow
// value class name, therefore we use lower-case
implicit def extendedMath(x: Double): ExtendedMath = new ExtendedMath(x)
}
object A extends HasMath
object X extends HasMath
object Test {
import X._
4.0.square
}

Scala: specifying a generic type that is constructible from another

I would like to do something which, more or less, boils down to the following:
def foo[S](x: String): S = S(x) // S(x) does not compile
So that if I have:
case class S1(x:String)
case class S2(x:String)
...
case class Sn(x:String)
I can write foo[Sx]("bar") to get Sx("foo").
Is there any way to specify that an instance of a class should be constructible from an instance of another (in this example String) and to actually invoke the constructor in a generic way?
You may use reflection (see #Ben Reich answer for detailed answer)
def foo[S:ClassTag](x: String): S = {
val runtimeClass = implicitly[ClassTag[S]].runtimeClass
val constructor = runtimeClass.<get a constructor with single String argument>
constructor(x) .asInstanceOf[S]
}
Or a type class that can construct an instance:
trait CanConstruct[S] {
def apply(x:String):S
}
def foo[S:CanConstruct](x: String): S = {
val constructor = implicitly[CanConstruct[S]]
constructor(x).asInstanceOf[S]
}
UPD You would need an instance of the type class for every type you wish to construct:
implicit val s1constructor = new CanConstruct[S1] { def apply(x:String) = S1(x) }
...
Also it seems to be the case for conversion functions:
implicit val convertStringToS1 = S1(_)
implicit val convertStringToS2 = S2(_)
...
Using reflection:
import reflect.ClassTag
def foo[S: ClassTag](x: String) = implicitly[ClassTag[S]]
.runtimeClass
.getConstructors
.map(a => a -> a.getParameters)
.collectFirst {
case (constructor, Array(p)) if p.getType == classOf[String]
=> constructor.newInstance(x).asInstanceOf[S]
}
Which will return an Option[S], if the proper constructor is found.
I sort of solved it with a macro:
object Construct {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def construct[A,B](x:B):A = macro constructImpl[A,B]
def constructImpl[A: c.WeakTypeTag,B](c:Context)(x:c.Expr[B]) = {
import c.universe._
c.Expr[A](q"""new ${c.weakTypeOf[A]}(${x.tree})""")
}
}
I now can write things like:
case class Foo(x:String)
case class Bar(x:Int)
construct[Foo,String]("foo")
construct[Bar,Int](42)
It would be nice to find a way to avoid having to write the second type parameter, though.

Scala: Multiple implicit conversions with same name

Using scala 2.10.3, my goal is to make the following work:
object A {
implicit class Imp(i: Int) {
def myPrint() {
println(i)
}
}
}
object B {
implicit class Imp(i: String) {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
This fails with
value myPrint is not a member of Int
If I give A.Imp and B.Imp different names (for example A.Imp1 and B.Imp2), it works.
Diving a bit deeper into it, there seems to be the same problem with implicit conversions.
This works:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
object MyApp extends App {
3.myPrint()
}
Whereas this doesn't:
object A {
implicit def Imp(i: Int) = new {
def myPrint() {
println(i)
}
}
}
object B {
implicit def Imp(i: String) = new {
def myPrint() {
println(i)
}
}
}
import A._
import B._
object MyApp extends App {
3.myPrint()
}
Why? Is this a bug in the scala compiler? I need this scenario, since my objects A and B derive from the same trait (with a type parameter) which then defines the implicit conversion for its type parameter. In this trait, I can only give one name for the implicit conversion. I want to be able to import more of these objects into my scope. Is there a way to do that?
edit: I can't give the implicit classes different names, since the examples above are only breaking down the problem. My actual code looks more like
trait P[T] {
implicit class Imp(i: T) {
def myPrint() {
...
}
}
}
object A extends P[Int]
object B extends P[String]
import A._
import B._
The implicits just have to be available as a simple name, so you can rename on import.
Just to verify:
scala> import A._ ; import B.{ Imp => BImp, _ }
import A._
import B.{Imp=>BImp, _}
scala> 3.myPrint
3
Actually, it works if you replace
import A._
import B._
with
import B._
import A._
What happens, I think, is that A.Imp is shadowed by B.Imp because it has the same name. Apparently shadowing applies on the function's name and do not take the signature into account.
So if you import A then B, then only B.Imp(i: String) will be available, and if you import B then A, then only A.Imp(i: Int) will be available.
If you need to use both A.Imp and B.Imp, you must rename one of them.
In case anyone else runs into this issue, there is a partial workaround, which I found here:
https://github.com/lihaoyi/scalatags/blob/3dea48c42c5581329e363d8c3f587c2c50d92f85/scalatags/shared/src/main/scala/scalatags/generic/Bundle.scala#L120
That code was written by Li Haoyi, so you can be pretty confident that no better solution exists...
Essentially, one can still use traits to define methods in terms of each other, but it will require boilerplate to copy those implicits into unique names. This example might be easier to follow:
trait Chainable
object Chainable {
implicit val _chainableFromInt = IntChainable.chainable _
implicit val _chainableFromIntTrav = IntChainable.traversable _
implicit val _chainableFromIntOpt = IntChainable.optional _
implicit val _chainableFromIntTry = IntChainable.tried _
implicit val _chainableFromDom = DomChainable.chainable _
implicit val _chainableFromDomTrav = DomChainable.traversable _
implicit val _chainableFromDomOpt = DomChainable.optional _
implicit val _chainableFromDomTry = DomChainable.tried _
private object IntChainable extends ImplChainables[Int] {
def chainable(n:Int) = Constant(n)
}
private object DomChainable extends ImplChainables[dom.Element]{
def chainable(e:Element) = Insertion(e)
}
private trait ImplChainables[T] {
def chainable(t:T):Chainable
def traversable(trav:TraversableOnce[T]):Chainable =
SeqChainable(trav.map(chainable).toList)
def optional(opt:Option[T]):Chainable =
opt match {
case Some(t) => chainable(t)
case None => NoneChainable
}
def tried(tried:Try[T]):Chainable =
optional(tried.toOption)
}
}
In other words, never write:
trait P[T] {
implicit def foo(i: T) = ...
}
object A extends P[X]
Because defining implicits in type parameterized traits will lead to these naming conflicts. Although, incidentally, the trait in mentioned in the link above is parameterized over many types, but idea there is that none of the implementations of that trait are ever needed in the same scope. (JSDom vs Text, and all._ vs short._ for those familiar with Scalatags)
I also recommend reading: http://www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html
It does not address this issue specifically but is an excellent summary of how to use implicits.
However, putting all these pieces together, this still seems to be an issue:
trait ImplChainables[AnotherTypeClass]{
type F[A] = A=>AnotherTypeClass
implicit def transitiveImplicit[A:F](t: A):Chainable
implicit def traversable[A:F](trav: TraversableOnce[A]):Chainable = ...
}
What this trait would allow is:
object anotherImpl extends ImplChainables[AnotherTypeClass] {...}
import anotherImpl._
implicit val string2another: String=>AnotherTypeClass = ...
Seq("x"):Chainable
Because of the type parameter and the context binding (implicit parameter) those cannot be eta-expanded (i.e: Foo.bar _ ) into function values. The implicit parameter part has been fixed in Dotty: http://dotty.epfl.ch/blog/2016/12/05/implicit-function-types.html
I do not know if a complete solution would be possible, needless to say this is a complex problem. So it would be nice if same name implicits just worked and the whole issue could be avoided. And in any case, adding an unimport keyword would make so much more sense than turning off implicits by shadowing them.