Why scala people don't like annotation?

2020-05-22 00:26发布

问题:

Attribute in .NET is a very popular feature. And Java added Annotation after 1.5 Annotations are used everywhere, see Java EE and Spring. But few scala library use annotation. lift-json don't use it. lift-record don't use it. Squeryl don't use it. subcut don't use it.(it has annotation for compiler plugin) ... Just named a few.

They use annotation only when they need some compiler magic. @tailrec, @inline, @BeanProperty, @Inject(in subcut) ...

Scala has super flexible type system, trait, implicit and Menifest[X]. So they don't need runtime meta-data?

Is there any scala project use annotation heavily?

p.s. I think the Dynamic should be annotation but not trait.

回答1:

In general, we don't use annotations because we don't really need them for a lot of things.

The few places I've seen annotations used:

  • Extra type systems (e.g. the CPS plugin for delimited continuations or the effect tracking plugin.
  • Interfacing with legacy Java interfaces. (scala-mojo-support)
  • Enforcing/enabling compiler optimisations, like @inline or @tailrec.

In Scala, we don't really need a dependency injection framework, as there's a few ways to do dependency injection that doesn't require an external tool. You can have the DI config separate from core code, but still be written in Scala. See: https://github.com/jsuereth/scala-in-depth-source/blob/master/chapter11/src/main/scala/scalax/config/Test.scala

So the basic answer is, there's nothing wrong with annotations, we just don't need them that often (yet).



回答2:

For me it's often an issue of compiler enforced type safety. Take a look at Squeryl and how it differs from a Java ORM like Hibernate. Where Hibernate would use an @Id annotation to denote a primary key, in Squeryl you create an id member, which is specified by the KeyedEntity trait. Methods that require an entity with a primary key (update & delete for instance) will crash loudly at compile time if one hasn't been defined. There are several other places within Squeryl where typesafe constructs replace annotations like collection mapping and date/time handling.

I think that this is a common way of thinking in the Scala community. Annotations are more of a run time feature and aren't as well regarded. The Scala compiler is very powerful and pushing more of your code into constructs it can validate makes sense for those who are willing to accept the complexity that comes along with that.



回答3:

You've pretty much answered your own question when you said that they're used for compiler magic. For the other uses it's usually some kind of runtime magic, where the two main tools are reflection and byte code transformation.

For the JSON case, the choices for the conversion would be the following: 1. A function or class that parses your JValue and builds your class T. 2. Reflection over the target classes to determine their layout and what is optional, then some "dynamic" code which runs over that parsed data to create and then eventually cast to the appropriate type.



回答4:

All the annotations like @tailrec and @inline are compile-time only. They extend StaticAnnotation, which is, AFAIK, the only annotation support in Scala, and they won't be retained at runtime. I think the philosophy is to avoid runtime annotations because those are retrieved via reflection, a world where the compiler can no longer help you beyond standard classes due to type erasure, but most importantly, because it's runtime and the goal is to decide what you're trying to decide at compile-time.

Look at using annotations to model a JSON output for example: In Java you would know if it works OK when you run your program. In Scala you could use type classes to model how each type is expressed in JSON. If you miss one definition, the compile will tell you. An excellent example is spray-json.

Dave Whittaker gives another great example in his answer.