Accessing a case class annotation - scala

case class FieldName(field: String) extends scala.annotation.StaticAnnotation
#FieldName("foo") trait Foo
import scala.reflect.runtime.universe._
symbolOf[Foo].annotations.head
// ann: Annotation = FieldName("type")
How do I access the annotation as a FieldName object? The doc mentions tree.children.tail, but there's no types to be had.

If you really want the instance of FieldName the best I can think of is using a ToolBox:
scala> case class FieldName(field: String) extends scala.annotation.StaticAnnotation
defined class FieldName
scala> #FieldName("foo") trait Foo
defined trait Foo
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> val annotation = symbolOf[Foo].annotations.head
annotation: reflect.runtime.universe.Annotation = FieldName("foo")
scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox
scala> val tb = runtimeMirror(getClass.getClassLoader).mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl#1b26a499
scala> tb.eval(tb.untypecheck(annotation.tree)).asInstanceOf[FieldName]
res10: FieldName = FieldName(foo)
With .tree.children.tail you can access the arguments passed to FieldName without creating the actual instance.
scala> annotation.tree.children.tail.map{ case Literal(Constant(field)) => field }
res11: List[Any] = List(foo)
If you just want all FieldName annotations and extract their value, you can do this:
scala> val fields = symbolOf[Foo].annotations.withFilter(
| a => a.tree.tpe <:< typeOf[FieldName]
| ).flatMap(
| a => a.tree.children.tail.map{ case Literal(Constant(field)) => field }
| )
fields: List[Any] = List(foo)

Related

Using cats.Contravariant typeclass

I'm trying to apply my Contravariant typeclass with syntax and faced the issue that it is not found. Here is what I currently have:
import cats._
import cats.implicits._
object Test {
type Foo[A] = A => Unit
private val f: Foo[String] = (_: String) => ()
implicit val cvar: Contravariant[Foo] = null
private val FF: Foo[Int] = f.contramap((i: Int) => //error: value contramap is not a member of Foo
String.valueOf(i)
)
}
I don't understand it. I provided implicit Contravariant[Foo], but the syntax is not applied anyway. What's wrong?
The mistake was that I did not extend the ContravariantSyntax. Removing implicit and mixing in it works as expected:
import cats._
import cats.syntax.ContravariantSyntax
object Test extends ContravariantSyntax{
type Foo[A] = A => Unit
private val f: Foo[String] = (_: String) => ()
implicit val cvar: Contravariant[Foo] = null
private val FF: Foo[Int] = f.contramap((i: Int) => //compiles - Ok!
String.valueOf(i)
)
}

Get generic value class parameter type via reflection

I want to get real Type of generic value class parameter. I tried:
import scala.reflect.runtime.universe._
case class Test[T](value: T) extends AnyVal
object Main extends App {
val tag = implicitly[TypeTag[Test[String]]]
val constructor = tag.tpe.members.collect {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.headOption
val constructorParams = constructor.map(_.paramLists.flatten).collect {
case param :: Nil => param
}
constructorParams.map(_.typeSignature).foreach(println)
}
but it prints:
T
I know that I can get type arguments with:
tag.tpe.typeArgs.foreach(println)
which outputs:
String
but Test class can be defined like that:
case class Test[T](value: List[T]) extends AnyVal
so type of type argument and parameter type are different.
How can I do this?
There's an operation in the compiler called "as seen from" which returns the type of a member with respect to a specific owner type.
Here's an example in a REPL session:
Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_102).
Type in expressions for evaluation. Or try :help.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> case class Test[T](value: List[T]) extends AnyVal
defined class Test
scala> val tp = typeOf[Test[String]]
tp: reflect.runtime.universe.Type = Test[String]
scala> val cls = tp.typeSymbol
cls: reflect.runtime.universe.Symbol = class Test
scala> val constructor = tp.members.collect({case m: MethodSymbol if m.isPrimaryConstructor => m}).head
constructor: reflect.runtime.universe.MethodSymbol = constructor Test
scala> constructor.info.asSeenFrom(tp, cls).paramLists.head.head.info
res0: reflect.runtime.universe.Type = scala.List[String]
scala> // alternatively
scala> constructor.infoIn(tp).paramLists.head.head.info
res1: reflect.runtime.universe.Type = scala.List[String]

Shapeless define lens for base trait

I have the next code
trait A { val id: Int }
case class B(id: Int) extends A
case class C(id: Int, name: String) extends A
i want to define common lens for all class hierarchy:
import shapeless._
import lens._
val idLens = lens[A] >> 'id
But i get error: could not find implicit value for parameter mkLens: shapeless.MkFieldLens[A,Symbol with shapeless.tag.Tagged[String("id")]]
Is it possible to define lens for all children of trait A?
shapeless doesn't provide an implicit conversion from A to Record. You could define LabelledGeneric[A] to provide the corresponding record type conversion:
import shapeless._
import lens._
import record._
import syntax.singleton._
trait A { val id: Int }
case class B(id: Int) extends A
case class C(id: Int, name: String) extends A
implicit val lgenA = new LabelledGeneric[A] {
type Repr = Record.`'id -> Int`.T
def to(a: A) : Repr = ('id ->> a.id) :: HNil
def from(r: Repr): A = new A { val id = r('id) }
}
val idLens = lens[A] >> 'id
val b = B(7)
println(idLens.get(b)) // 7

Test if c.universe.Type is assignable to another type in a macro

I'm attempting to write a macro which takes a class with a java bean interface and a case class and creates a pair of methods for mapping between them.
I am attempting to check that the types match for each property, however the types in the java bean are e.g. java.lang.Long and the types in the case class are scala.Long.
My question is, given the c.universe.Type object for these 2, is there a way to test if there are implicit conversions between them? i.e. to test if I can pass one to a method which expects the other.
If you want to check whether an implicit conversion exists, you can use c.inferImplicitView.
Proof of concept:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def test[T,S,R](value: T, func: S => R): R = macro Macro.myMacro[T,S,R]
object Macro {
def myMacro[T: c.WeakTypeTag,S: c.WeakTypeTag,R](c: Context)(value: c.Tree, func: c.Tree): c.Tree = {
import c.universe._
val view = c.inferImplicitView(value, weakTypeOf[T], weakTypeOf[S])
if (view == EmptyTree)
c.abort(c.enclosingPosition, "Cannot apply function")
else
q"$func($value)"
}
}
// Exiting paste mode, now interpreting.
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
defined term macro test: [T, S, R](value: T, func: S => R)R
defined object Macro
scala> test(3L, (l: java.lang.Long) => l.toString)
res20: String = 3
scala> test(3L, (l: java.lang.Integer) => l.toString)
<console>:23: error: Cannot apply function
test(3L, (l: java.lang.Integer) => l.toString)
^
If you don't have a value, apparently it also works if you do c.inferImplicitView(EmptyTree, weakTypeOf[T], weakTypeOf[S]).
A more complex example closer to the Actual Problem:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
def mapBetween[JB,CC](jb: JB): CC = macro Macro.myMacro[JB,CC]
object Macro {
def myMacro[JB: c.WeakTypeTag, CC: c.WeakTypeTag](c: Context)(jb: c.Tree): c.Tree = {
import c.universe._
val jbTpe = weakTypeOf[JB]
val ccTpe = weakTypeOf[CC]
val constructor = ccTpe.members.filter(m =>
m.isConstructor && m.name != TermName("$init$")
).head.asMethod
if(constructor.paramLists.size != 1 || constructor.paramLists.head.size != 1)
c.abort(c.enclosingPosition, "not supported :(")
val ccParam = constructor.paramLists.head.head
val ccParamType = ccParam.typeSignature
val ccParamName = ccParam.name.toString
val jbGetter = jbTpe.member(TermName(s"get${ccParamName.head.toUpper + ccParamName.tail}"))
val getterType = jbGetter.asMethod.returnType
val view = c.inferImplicitView(EmptyTree, getterType, ccParamType)
if (view == EmptyTree)
c.abort(c.enclosingPosition, "Cannot apply function")
else
q"new ${ccTpe.typeSymbol.name.toTypeName}($jb.${jbGetter.name.toTermName})"
}
}
// Exiting paste mode, now interpreting.
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
defined term macro mapBetween: [JB, CC](jb: JB)CC
defined object Macro
scala> case class CaseClass(foo: Int)
defined class CaseClass
scala> class JavaBean{ def getFoo(): java.lang.Integer = 42 }
defined class JavaBean
scala> mapBetween[JavaBean,CaseClass](new JavaBean)
res0: CaseClass = CaseClass(42)
scala> case class CaseClass(foo: Int)
defined class CaseClass
scala> class JavaBean{ def getFoo(): java.lang.Double = 42.0 }
defined class JavaBean
scala> mapBetween[JavaBean,CaseClass](new JavaBean)
<console>:27: error: Cannot apply function
mapBetween[JavaBean,CaseClass](new JavaBean)
^

How to get constructor arguments in a method using typetags/mirrors?

For Case Class:
case class MyClass(param1: String, param2: String)
Why does this reflective method:
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.{ universe => ru }
def getSettings[T](paramObj: T)(implicit tag: TypeTag[T]) {
val m = ru.runtimeMirror(getClass.getClassLoader)
val classType = ru.typeOf[T].typeSymbol.asClass
val cm = m.reflectClass(classType)
val constructor = tag.tpe.declaration(ru.nme.CONSTRUCTOR).asMethod
val constructorMethod = cm.reflectConstructor(constructor)
val args = constructor.asMethod.paramss.head map { p => (p.name.decoded, p.typeSignature) }
println(args)
}
When Invoked like so:
scala> getSettings(MyClass)
List()
Have that output?? Is the constructor not 2 arguments??
I must be missing something really obvious here. The documentation doesn't cover scenarios quite like this.
http://docs.scala-lang.org/overviews/reflection/overview.html
getSettings(MyClass)
MyClass is the companion object of class MyClass. It has no constructor parameters.
You should rewrite your code like this:
def getSettings[T]()(implicit tag: TypeTag[T]) {
...
}
scala> getSettings[MyClass]
List((param1,String), (param2,String))