Hey! I’d love to hear your thoughts on the default...
# language-evolution
m
Hey! I’d love to hear your thoughts on the default applicability of annotations. You might’ve heard that we’re planning to slightly adjust how annotations are applied by default. For example, in a class like
Person(@Email val email: String)
, the
@Email
annotation would be applied to both the constructor parameter and the field, while today it only applies to the parameter. This change seems safe and reasonable, and was originally motivated by KT-19289 At the same time, we’ve been considering a more aggressive default: applying annotations to all applicable targets. For instance, in
@Anno var x: String
, the annotation could be applied to the property, backing field, getter, setter parameter, and so on. This is similar to how annotations work for records in Java. Initially, we were cautious about this idea, we thought it might break some frameworks. But based on recent conversations, it seems this concern may not be as serious as we thought. Perhaps we should move toward this broader default after all. So I wonder if you could share your thoughts and any known pitfalls of changing the default. What do you think?
c
To clarify, with this new idea, it would still be possible to explicitly apply the annotation only to a single thing via the
@property:Anno
syntaxes?
m
Yes. To use a specific target, it'll be possible to use
@property:
👍 2
e
One datapoint in favour of the “aggressive” one: It seems like the sensible default and it still surprises me that it wasnt. Only pitfall (I can think of) would be java (or interop) annotation processing that handles the same annotation differently if it appears on different parts of a proprty (eg getter setter field). IMO these tools should instead understand & support Kotlin via kotlin metadata jvm
thank you color 1
j
It is not intuitive at all IMO.
Basically looks like a patch for a third party framework, adapting to all frameworks in a general way is not very good IMO. It would be better to have a "toggle like" option when working with those frameworks to get that behavior.
e
Which one is not intuitive @Javier? (I didnt get it from your message)
m
Sorry, I didn’t get your point. What do you mean by 'that'? What exactly is not intuitive, and for whom? Could you please elaborate?
j
Oh, sorry, I re-readed it
I was thinking the whole constructor was annotated
Ignore my comments 😛
z
I work on a few annotation-based compiler plugins and think this makes sense 👍. I have more than one bit of awkward logic to basically search all 4-5 places a property may be holding annotations 😛
thank you color 1
3
Some good deduping APIs in the compiler would be helpful for such cases
y
Just checking. In
@Anno var x: String
, the annotation won't be applied to the type, right? That's the only place where I can see issues. There's realistic scenarios I think where
@Anno var x: String
and
@Anno var x: @Anno String
mean different things
👌 1
m
Yes, you're right. Annotations won't be applied to a type automatically
👍🏼 1
h
I also heavily use annotations in my plugins in function parameters, getters and constructors and the latter is confusing as a user as well as a plugin author to check all possible locations:
class A(@Foo val s: String)
and
class A(@property:Foo val s: String)
. It is easy to forgot the
property:
prefix (because some plugins do not lookup all locations).
m
Thanks! ^ So I see that applying annotations to more places is useful, but do you think it might break existing frameworks or plugins if annotations start appearing in more places than they do now?
h
IMHO it really depends on the plugin and the use-case/implementation. For example, we extract the names (and types) of all annotated properties of a class, so we put them into a set/map, the order is not required. And I guess, you often need the parameter name as some key/information.
z
I could see it potentially breaking plugins that don’t dedupe annotations and potentially interpreting them as repeated
1
r
Just to clarify, would that mean that
Copy code
@JvmName("bob") var fred: String
would be roughly equivalent to this in Java:
Copy code
String bob;

String bob() {
    return this.bob;
}

void bob(String bob) {
    this.bob = bob;
}
If that's the case, and there are multiple conflicting annotations, which one wins? For example:
Copy code
@get:JvmName("getBob")
@JvmName("bob")
var fred: String
Does the latest one take precedence? Or perhaps the higher specificity?
m
@Ruckus This case won’t work, as
@JvmName
doesn’t have the
PROPERTY
target, so you still have to use `get`/`set` targets. As for the more general case, I believe we should preserve the current behavior, so annotations with a use-site target take precedence
👍 1
👍🏾 1
j
IMO applying to all targets is the only intuitive result. Annotation processors should adapt to that behavior, rather than compilers trying to guess what was intended. This would dovetail nicely with the long-standing feature request for external annotations - we could add a kotlin-specific
@PreferredTarget
annotation to the annotation class when necessary that way.