What is the (current) state of scala's reflection capabilities, especially wrt annotations since version 2.11?

scala seems to be a great addition to the JVM universe. It reminds me of a weird hybrid of C ++, C #, and Swift nested in the JVM world.

However, many scala features may not be available due to missing or outdated documentation.

This is especially true of reflection capabilities.

For example, I evaluate whether it is possible to extend scala classes at runtime or compilation using scala annotations. I am using the latest version of scala version 2.11. As a motivating example, suppose I'm doing a case class SimpleAnnotation() extends StaticAnnotation . At runtime, I would like to find all case class es with this annotation.

This is probably the most typical and vanilla use case for annotations.

In C # and in Java, it is relatively easy to determine at runtime whether a given class is annotated. This is a canonical view of a use case with a canonical answer. However, in scala, I don’t understand what I have to do to achieve this behavior or even whether it is possible. In particular, after scanning any previous material on the annotation and reflection of scala, I can only ask a question:

  • Is it possible?
  • Is this possible only at runtime or runtime?
  • Is this only possible before or after scala version 2.10?
  • Is this only possible with Java annotations in scala classes?
  • Why getClass[AnnotatedClass].getAnnotations return such seemingly distorted information?
  • Why are macros and reflection seemingly configured in scala?

Any guidance is appreciated ... and I'm sure I'm not the only one who is embarrassed.

+2
source share
1 answer

Reflection and Macros use many APIs because they are basically the same thing: Meta Programming. You can create and execute code, you must reflect types, etc. Of course, there are some differences: at compile time you cannot display instances of the runtime and at runtime you do not get access to the internal structure of methods, scope and other information that is deleted at compile time.

Both APIs are still experimental and are likely to change in some parts in the future, but they are very useful and well documented enough. Scala, being such a universal language, they are much more complex than the API in Java.

In this documentation you are very far:

http://www.scala-lang.org/api/2.11.7/scala-reflect/

http://www.scala-lang.org/api/2.11.7/scala-compiler/

http://docs.scala-lang.org/overviews/ (bottom of page)

This getClass[AnnotatedClass].getAnnotations gives you only Java annotations to get Scala Annotations, you need to get a Scala type instead of a class.

Access to reflections is possible at runtime, as well as during compilation, however there are three types of annotations:

  • Simple annotations that are only in code: they can be accessed from macros in the compilation unit, where the macro is called where the macro accesses the AST

  • StaticAnnotations, which are divided by compilation modules: they can be accessed through the Scala reflection api

  • ClassfileNnotations: they represent annotations stored as java annotations. If you want to access them through the Java Reflection API, you have to define them in Java, though.

Here is an example:

 @SerialVersionUID(1) class Blub 

Now we can get the annotation as follows:

 import scala.reflect.runtime.universe._ val a = typeOf[Blub].typeSymbol.annotations.head 

What we actually get is not an instance of annotation. The runtime simply gives us what is written in byte code: the Scala code generating the annotation. You can print the AST you receive:

 showRaw(a.tree) 

Now this is already a rather complex structure, but we can decompose it using pattern matching:

 val Apply(_, List(AssignOrNamedArg(_,Literal(Constant(value))))) = a.tree val uid = value.asInstanceOf[Long] 

This is normal for very simple annotations (but we could write them in Java and rely on JVM instances for us). What if we really want to evaluate this code and instantiate an annotation class? (For @SerialVersionUID this will not help us, since the class does not actually give access to id ...) We can also do this:

 case class MyAnnotation(name: String) extends annotation.ClassfileAnnotation @MyAnnotation(name = "asd") class MyClass object MyApp extends App { import reflect.runtime.universe._ import scala.reflect.runtime.currentMirror import scala.tools.reflect.ToolBox val toolbox = currentMirror.mkToolBox() val annotation = typeOf[MyClass].typeSymbol.annotations.head val instance = toolbox.eval(toolbox.untypecheck(annotation.tree)) .asInstanceOf[MyAnnotation] println(instance.name) } 

Note that this will call the compiler, which takes a little time, especially if you are doing this for the first time. Sophisticated metaprogramming must be performed at compile time in Scala. Many things in Java are executed only at runtime, because you only have meta-programming of the runtime (well, there are processors with annotations, but they are more limited).

+5
source

Source: https://habr.com/ru/post/1243106/


All Articles