Modifiers vs Annotations

This is another heads-up and a call for feedback. We have been discussing options regarding Kotlin’s annotation syntax for quite some time already, rolling out experiments, gathering feedback. As we are finalizing the language now, many pain points that we used to postpone are surfacing. We have to make decisions, and sometimes in a defensive way. In this post I will give an overview of the options we have and the decisions we are provisioning.

Setting the stage: Some introductory definitions

Kotlin (as well as many other languages) has two kinds of metadata:

  • modifiers (such as public, open or abstract), which are built into the language, and
  • annotations (such as @Test or @Inject), which are defined in libraries, also they can have parameters.

Unlike many languages, in Kotlin most modifiers are not proper keywords. They have special meaning only where they are applicable, i.e. in front of a declaration. The compiler won’t mind if you call your variable or class public:

This technique is known as “soft keywords” or “contextual keywords”.

The dream: Unified metadata

When Kotlin was conceived I personally was fascinated with the idea of unified metadata: I thought that we should reach a point where there’s no distinction between modifiers and annotations. All metadata should be declared explicitly, and treated equally. It is uniform, extensible and otherwise great! Both language designers and users can define annotations and extend the language thereof. In my ideal world there would be no modifiers, only annotations, i.e. public would be an annotation, as well as inline, abstract, enum etc.

Then it turned out that technically it was a lot easier to introduce modifiers (and not annotations) at the early stage of the language implementation, so we did that as a temporary measure.

This is why Kotlin allows annotations without @: I wanted them to look like modifiers to be able to turn modifiers into annotations later. Many things that you might have thought were modifiers are actually annotations: data and inline are the most popular ones.

The reality: Tooling matters

Now, I have to admit that my dream of unified metadata, while not entirely impossible to implement, turned out to be impractical.

Modifiers have a peculiar property of being available right after the parsing stage. And this is important to some performance-critical tasks (e.g. in the IDE), because they sometimes rely on knowing, for example, what’s public and what’s not in contexts where nothing is ready but a parsed text.

So, modifiers have to stay.

Annotation syntax

Now, the initial idea is challenged: if we don’t unify modifiers and annotations, is there a point in keeping their syntax so similar?

On the one hand, it looks kind of cool that we can omit the “@” before annotations. One can argue that it reduces the amount of “noise” in the code.

On the other hand, there are a bunch of problems, none of which is really critical, but they are annoying enough:

First, it is not orthogonal: we can either use “@” in front of annotations or not. It’s a matter of convention most of the time. But sometimes we have to use “@”: local classes and functions are the most notorious example.

Then, it complicates error recovery: it’s alright when the code is correct, but when you are typing in the IDE, the parser must be able to recover from errors and recognize the code structure on incomplete code. And random identifiers being parseable as annotations complicate this quite a bit.

Also, it complicates naming conventions: we used to name annotations with the first letter in the lower case, like @inline or @platformStatic. Many people find camel-case multi-word names like the latter one ugly in this context. So, there are proposals to use lowercase only for one-word annotations (that look like modifiers then). And also Java annotations (many of which are used frequently) are still named with the upper case first letter. Altogether, it’s a mess. A small one, but still a mess.

And a more technical, but nevertheless realistic concern: it complicates language evolution. We can’t add a new modifier “foo” to the next version of Kotlin without possibly breaking someone’s code: if some library has an annotation foo, it will clash, and the best the compiler can do is report an error.

So, we think that the benefit is too little for the cost.

Rich modifiers

Incidentally, we think that it would make sense to allow modifiers to have parameters when needed. It facilitates language evolution (we can add optional parameters to existing modifiers later) and doesn’t seem to introduce any problems. For example, annotation is a modifier (it is critical to know annotation classes before resolving names), but it has parameters:

Conclusion

We are going to require all annotations to be prefixed with “@”.

Incidentally, some annotations will be turned into modifiers: for example, data and inline.

Your feedback is welcome: we would like to know if we are missing something here, in your opinion.

Special thanks to the author and contributors to this forum thread.

About Andrey Breslav

Andrey is the lead language designer of Kotlin at JetBrains.
This entry was posted in Language design. Bookmark the permalink.

44 Responses to Modifiers vs Annotations

  1. HE Guangyu says:

    I think that’s ok, I like it.

    Thank you for creating such a fascinating language.

  2. Mike Strobel says:

    Consistency is a desirable quality in a language, I would say. I think you made the right decision by requiring annotations to be prefixed, always.

  3. Olivier Binda says:

    Making things simpler/better for parsers/ide while retaining (or gaining later on) functionnalities at the cost of a single char sounds really fine to me.

    Also, if modifiers (that can have parameters) are some kind of annotations that are available earlier in the build pipeline, it’s some kind of uniformity, right ?

    Another way to see the slight difference might just be that modifiers are particular annotations that have stood the test of time and proven to have a universal/popular usefullness

    Who knows, maybee some day, some other new annotations will prove really usefull/popular and ascend to modifyer status like a :
    @testable annotation that allows tests on otherwise private/protected stuff
    @inject/@provide that does dependency injection…
    @UiThread…

  4. Eddie Ringle says:

    I originally posted this as a comment to the Reddit thread for this post, but I’ll copy it here as well:

    It’s disappointing, to me, that it seems like they’re sacrificing design tenets of the language due to what are essentially tooling limitations.

    Modifiers have a peculiar property of being available right after the parsing stage. And this is important to some performance-critical tasks (e.g. in the IDE), because they sometimes rely on knowing, for example, what’s public and what’s not in contexts where nothing is ready but a parsed text.

    If you have parsed modifiers, haven’t you also parsed the annotations as well as the import statements, giving you fully-qualified names to work with? (Example: What’s stopping the tools from treating the kotlin.inline annotation differently from a modifier named inline?)

    A language shouldn’t be influenced by the tasks an IDE is responsible for. Rather, the IDE should be built for the language.

  5. Siarhei says:

    What is the fate of [square] brackets syntax?

  6. I kinda liked the @-less syntax because it made annotations look like proper language elements. However I support Kotlin’s design principle of making important things explicit and also this reduces the potential for inconsistent code styles: annotations with or without @ are no longer a matter of convention.

  7. Yoavst says:

    “We are going to require all annotations to be prefixed with “@”.”

    So my code is going to be ugly again :(

  8. Rob Bygrave says:

    I’m with Mike on this. Consistency is important and I very much like the conclusion. I also expect to see mixed Kotlin/Java code bases in my future and so as an extra benefit I’ll see consistency across both of those languages which is going to be very good and just reduces the ‘multi-langauge friction’ a little bit (good when adoption occurs with existing java code bases).

    My other thought is that java modules (jigsaw) is coming down the track. So I think Java will add “modules”, “requires” and “exports” which look like modifiers to me. I hope Kotlin can be consistent there.

  9. Eugenio Marletti says:

    I don’t care a lot how things look like as long as there’s ways to extend the language.
    By the way, what’s planned for that?

  10. Mykola says:

    good to hear you finally decided to prefix all annotations with @
    +1 voice for that

  11. Pavel says:

    +1 for @ before all annotations
    +1 for parametrized modifiers
    Don’t clear for me why data or inline should be modifiers.

    • The rationale is that if it changes the way things are checked by the compiler, it should probably be a modifier. But this is not finalized yet.

      • Christian says:

        Given the presence of annotation processors in Java as, effectively, a plugin to the compiler that changes the way some things are checked (e.g. we turn otherwise legal java into compiler errors if used with a framework like Dagger in incorrect ways), I’m not sure the distinction is really valid, given that rationale. Have to think about it more, but certainly annotations change compiler behavior all the time, in our world.

  12. Mohammad Shamsi says:

    I was waiting for this change and really like to idea of using always “@” for annotations.

    In addition to given reasons in the blog and comments, the @-less syntax might only look nicer for simple annotations like “data” or “inline” but with more complex annotations it doesn’t look nice and is not easy to follow and read.

    Take for example, @Column annotation in JPA:

    Without a Java IDE, it is not easy to say if first line is annotating the second line or they
    are individual statements.

    • fanchao says:

      Agreed. Complex annotation will read like a function call. @ is clearer.

      • Yoavst says:

        <code class="kotlin"var column(name="t_cost", updatable=false, precision=12, scale=2) cost: BigDecimal = 0

      • Yoavst says:

        <code class="kotlin"var column(name="t_cost", updatable=false, precision=12, scale=2) cost: BigDecimal = 0
        I agree that it doesn’t look nice for long annotation, but for short it looks great.

  13. Lionel says:

    Glad this is happening. I’ve been using ‘@’ as a matter of style anyway. I found annotations without the @ was actually making the code harder to read as without the IDE highlighting it was harder to see what was actually code. Also using the @ helped the autocomplete to actually function.

  14. Sven Jacobs says:

    Consistency is important. I like your suggestion that annotations must always be prefixed with @ and that some existing language annotations become modifiers. Also I suggest that annotations that are bundled with Kotlin, if some remain, are written in @CamelCase with a capital first letter.

  15. Oliver Plohmann says:

    I understand the pain from having to give up a good idea. Given the situation I think Java-style makes good sense with first letter also being upper case. Feels more “interoperable”. If it’s the same thing in the end then let it look the same way. About Kotlin-internal annotations it depends a bit what the plans are concerning AST transformations. Hard to say. In Groovy they did everything with AST transformation with Java-style annotations. By the way, @platformStatic will also become a modifier?

  16. If you do keep some of those annotations as modifiers, do you.plan to make the tail recursion one a modifier, or would that definitely be an annotation?

  17. Amir Abiri says:

    Having tried to write Kotlin code for spring boot, the camel casing bites. So in that respect it’s a welcome change.

    OTH it’s nice to be able to create “modifiers” like data or inline. Specially if they are intended for consumption by an annotation processor.

    Perhaps one possible solution is to make the distinction between modifiers and annotation formal, and allow user-defined modifiers, perhaps with some limitations (for example not retained in runtime and only used by annotation processors, etc).

    • Christian says:

      I think that would conflict (stylistically) with the very common cases of interoperating with java frameworks that use annotation processors, since you would already have code not consistent with this convention running around your codebase.

  18. David Leppik says:

    Personally, I like having the @. I prefer to know what’s a language feature and what’s part of a library. (This is one of the things Scala got wrong and Perl got right.) I want to know where features are coming from; I’d rather err on the side of having keywords that start with @ than be lead to believe that a third-party feature is part of the language.

    Good design is about making important distinctions visible and hiding unimportant distinctions. Some people might think that language feature vs. library feature isn’t an important distinction. That’s true, if the library is (a) always there, (b) just as reliable as the language, and (c) documented in the same place. For example, if your customer tells you to target DOM/JavaScript in IE 6, and they promise they’re never, ever going to upgrade!

  19. Michael Pardo says:

    This is a welcome change since the modifier/annotation confusion and optional annotation syntax (@, []) is something that has bugged me for a while. As a convention we require “@”-prefixed annotations, so I’m glad to see the language headed in the same direction.

  20. Dmitry Zaytsev says:

    +1 for @
    +1 for consistency

    However,


    Incidentally, some annotations will be turned into modifiers: for example, data and inline.

    I somehow missed how this became a conclusion. If @data and @inline were annotations all this time, what is the point of making them modifiers? I find it to be less consistent. I perceive those as Kotlin-library-features rather than features of language itself. What makes them so special?

    • I perceive those as Kotlin-library-features rather than features of language itself.

      And they are not. This is why we should make them modifiers. inline is a language feature with many involved consequences, data also imposes extra checks and changes in semantics

  21. Sergey Igushkin says:

    By the way, as the matter comes to the annotations, isn’t there a proposal for better syntax of array annotation parameters?

    E.g. Hibernate’s @OneToOne(cascade = arrayOf(CascadeType.ALL)) looks quite clumsy, what about some vararg syntax for such cases?

  22. Edu Garcia says:

    Just to add my voice to the discussion, +1 on the @ being required. I think consistency is very important, and the point about this complicating naming conventions is a good one (also, it will be nice if you try hard to avoid those kind of naming messes in the future no matter what the feature, even if it makes Kotlin a bit “uglier”)

  23. Norbert Sándor says:

    This is a good decision, this makes the language more readable, and more consistent with Java.

    What I don’t like: making eg. “data” a modifier, therefore making it part of the core language.
    Take a look at the Xtend language, they made it right: @Data is a normal annotation, and an active annotation processor modifies the class to change its semantics (in a simple AST-transformation-like compilation step). This works even in the IDE, on-the-fly while typing!
    Take a look at
    – the built-in active annotations like @Data, @Accessors, @Delegate (Active annotations)
    – and the ones provided by the community like @Cached, @Buildable, @ExtractInterface (xtend-contrib)

    I think this is the idea I miss most from Kotlin. Many modern languages have some kind of source code processing feature: Xtend active annotations, Scala macros, Groovy AST transformations (, Ceylon AST transformers).
    Kotlin began to support Java annotation processing but it is an “old-style” and very limited solution.

  24. Suminda Dharmasena says:

    I believe you can use annotation in the place of modifier. So you can deprecated the modifiers and make them all annotations. This might sound radical but it reduces the surface area of the language where you have the same infrastructure as annotation processing to handle the modifiers which you will any way have. Also this can be further leveraged to provide more granularity and extensibility to the modifiers than when it is baked into the language. Modifiers are a legacy from old languages and from days when there were no annotation in the languages. A modern language designed for the future need not carry this technical debt.

    For this to succeed you need a strong annotation processing framework coupled with a strong checking framework / code contract framework.

    So best cause of action is to deprecate and remove all modifiers and make them annotation. Break this while you can, i.e., before V1.

  25. Suminda Dharmasena says:

    Make the @ usage optional unless there is an ambiguity.

  26. HE Guangyu says:

    May I ask a question?

    In kotlin, How to change a Short variable into ByteArray?

    in Java, we can write:
    static byte[] toByteArray(short i) {
    byte a[] = new byte[2];
    a[0] = (byte)(i>>8);
    a[1] = (byte) i;
    return a;
    }

    but in kotlin, Short don’t support shr operation.

    • In Java short doesn’t support a right shift either. It’s first implicitly converted to int, and then shifted. You should do the widening conversion explicitly in Kotlin by using toInt()

  27. Mykhailo says:

    +1 for separating annotations and modifiers
    +1 for mandatory @
    I was very confused by similarity of modifiers and annotations in kotlin. If both features have to stay then separating them on a syntax level is a good idea.

  28. Suminda Dharmasena says:

    Best of both is give the ability to define your modifies. Under the hood modifiers will be treated as annotation and processed using the same infrastructure.

    • I don’t think you’re reading what is there:

      Modifiers have a peculiar property of being available right after the parsing stage. And this is important to some performance-critical tasks (e.g. in the IDE), because they sometimes rely on knowing, for example, what’s public and what’s not in contexts where nothing is ready but a parsed text.

      [inline and data] are not [Kotlin library features]. This is why we should make them modifiers. inline is a language feature with many involved consequences, data also imposes extra checks and changes in semantics

  29. Pingback: Kotlin M12 | Kotlin.es

  30. Vladimir says:

    I like your final design. For me it was weird that annotations can optionally omit “@”. I used “@” anyway, like I do in Java.

Comments are closed.