Make square brackets mandatory for annotations


#21

There're obvious trade-offs, so we'll think about it. Thanks for your effort, guys.


#22

To give you some more details, here are the trade-offs we are considering, some of them were mentioned in this discussion:

  • having two syntactic forms for the same construct is something to consider very carefully,
  • having a clean syntax for annotations like data, volatile etc is appealing
  • but maybe we could as well use modifiers there
  • but modifiers can not have optional parameters, and we have to address issues like http://youtrack.jetbrains.com/issue/KT-4503
  • having to put square brackets around annotations on local declarations and expressions is unfortunate
  • we can not reuse "@" for annotations now, because there are qualified retuns and this, but maybe we can find another syntax for them
  • maybe we can have a special kind of annotations that support modifier syntax (data, volatile) and require brackes for all other annotations?

#23

FWIW I do appreciate the clean syntax of things like data class quite a bit. @data class wouldn't be as readable. If there was a way to avoid the need for square brackets in the remaining cases, that'd be a good fix but I have no idea what kind of issues that'd raise in the parser.


#24

-1 for me.  Test fun blahblah is just so pretty and I love the intellij highlighting.


#25

I agree, this works so nice for DSLs. I'd consider making them mandatory only for non-empty argument lists,

  e.g.[ ann(foo) ]


#26

I agree with all your points. Perhaps it's over complicated but one solution would be a combination of your last point (allowing annotation author to allow/disallow use without brackets), combined with a compiler option to disallow use of other annotations without brackets. This would give:

  • Consistent use of libraries - if annotation author says it can be used without square brackets, this is always allowed (eg. for data annotation)
  • Individual projects can enforce consistent use of brackets for all other annotations if they prefer this style

Personally I would like to enforce use of brackets on my projects (if I had any… hopefully one day :wink:

Alfie.


#27

Interesting suggestion. But this would not help to avoid the confusion completely. Look at Scala: some developers of libraries are responsible and others come up with methods like #%~> (no kidding!). So, with your suggestion, some developers would decide to force brackets and others wouldn't. And, anyway, you could have confusing cases and even don't know, that you're just reading an annotation.

Keep it simple, make brackets mandatory!


#28

+1 make brackets mandatory!

We should learn something from Scala , it’s method name looks like a black hole .


#29

When I was suggesting to make square brackets mandatory, I had Scala in mind. The methods/operartors are a nightmare in Scala! But there is more, that makes the language sometimes hard to reason about: method calls with or without parenthesis and dots, objects looking like methods, literals and usual objects magically converted to something else, and so on. I'm a strong advocate of making things simple an clear.

  • Nobody should ever wonder about magic words in Kotlin that are actually annotations.
  • Nobody should think an annotation with parameters would be a method call.
  • No team should ever discuss, whether their style requires brackets or not.
  • No compilation error should ever be reported due to missing brackets in special cases.

#30

We are looking if we want to add an annotation to allow functions to be called in infix form.

fun A.member(b: B)
infix fun A.action(b: B)


A() member B() // error
A().member(B()) // ok
A() action B() // ok
A().action(B()) // ok

Probably we can try something similar, “nobrackets” is of course a placeholder:

public annotation class Inject
public nobrackets annotation class test

Inject fun fn() // error [Inject] fun fn() // ok test fun fn() // ok [test] fun fn() // ok

What do you think?


#31

+1 for requiring an annotation to allow a function to be called using infix form. After using Scala I'm not a fan of infix functions. Personally I'd be happy if they weren't supported at all. But if we have to have them an annotation seems like a good way to limit inconsistencies in coding styles.


#32

+1 for requiring an annotation to allow a function to be called using infix form. After using Scala I'm not a fan of infix functions. Personally I'd be happy if they weren't supported at all. But if we have to have them an annotation seems like a good way to limit inconsistencies in coding styles.

I'd say that code with infix notation often looks better. But I'd also say that looking better doesn't imply being better, or more readable. Without an infix notation, I can visually scan for dots to find all method calls (ignoring properties for now).

Personally I'd be happy if they weren't supported at all.

Me, too. Actually, “there’s more than one way to do it” is nearly always a bad thing.

I’d be happy with an IDE mode enforcing all the boring Java stuff: dots in methods calls, semicolons after statements, …


#33

Could you elaborate on why you dislike infix functions? I don't have any Scala experience so I'm interested in the downsides.


#34

There are a couple of reasons. The first is readability. Compare the following lines:

foo.bar(baz) foo bar baz

In the first one it's immediately obvious I'm calling a method called bar on an object called foo, passing an object called baz as an argument. The second is much less obvious to me, I have to think about what's happening.

The other reason is consistency. If you have two ways to do the same thing, it’s inevitable some people will prefer one and some will prefer the other. Which leads to inconsistencies across the codebase. This is why I’m in favour of requiring an annotation for infix functions. It still doesn’t stop people using dot notation for infix functions though so there’s still scope for inconsistency.

It also seems odd and inconsistent to have a special sytax that’s only available to single argument functions:

foo fun1 123 // function with 1 argument foo.fun2(123, 456) // function with 2 arguments, totally different syntax


#35

Also, infix functions add complexity to the language and I'm not sure they give us much in return. They allow map literals without adding syntax to the language which is nice. They also allow more natural looking DSLs, but I'm not a big fan of DSLs so I don't regard that as a particularly good thing.


#36

I actually think DSLs are the main justification for infix, and in this context they're very useful. I totally agree with the other comments though about readability for general code / teams and having more than one way to do the same thing leading to inconsistency. So I'd vote to disable it in general and only allow by exception.

One of Kotlin’s goals (viewing the presentations) is providing a productive but safe environment for large teams, and when there are multiple code styles for the same semantics this is always a problem IMO, and coding guidelines only go so far.

Alfie.


#37

Agree that making them mandatory would be simplest, but as others do I like the simple syntax like "data class Xyz".

But I really hate the method confusion for annotations with parameters!

Some more possible options:

  • Change the syntax for parameters, eg: myannotation{name=“food”}
  • Give flexible compiler options with sensible defaults, so teams can choose their preference. Options should include never allow without brackets, always allow, allow only if lib author flagged it, only allow if no parameters

I agree somewhat with Chris Helmbold’s comment though that if a team has to think about it, it’s a problem… so controlling with compiler options not ideal.


#38

I'll add my two cents. I believe that, since Kotlin is supposed to be used with an IDE (like any modern programming language), there is no point in adding square brackets around when the IDE colors the annotation in a different way, especially when there is no syntactic ambiguity (i.e., on function and property/field declarations).

I would rather require them around multiple parameter annotations, where they would look like function call. I would even go further and use for a different syntax.
Instead of:

``

[foo(bar=baz)] fun hello() = "hello"


I propose

[foo bar=baz] fun hello() = "hello"
there should be no ambiguity since this special syntax only occurs within brackets, and in "annotation position".

I am not sure about explicit opt-in for the no-brackets sytnax, since it would disallow the syntax for Java annotations, and special casing only for Java would be less elegant

As for infix functions, please do not do away with them. I find them really convenient and see no harm in them. The problem with Scala is not really with infix operators, rather with operator overloading and possibly null-parameter lists, and a general lack of guidelines in the way things should be done. For instance, consider the following:

``

val m = Map( “ten” -> 10 )
m(“ten”) get() // – compiler error here

Why is there a compiler error for method get() ? Because get has a nullary parameter list, thus it should be invoked like so:

m("ten") get

this gave me quite an headache yesterday. The other problem is with operator overload[1], which Kotlin IMO is already addressing in the right way

Situations like these are IMO the main problem with Scala APIs.

A limited use of infix operators can make an API a breeze to read and use. I’m currently working on a port of the Squants library, called Kuants (still WIP), for which a limited use of infix operators (the to() method) makes the API much clearer.

[1] e.g. see Scala Graph http://www.scala-graph.org/guides/core-initializing.html


#39

I strongly dislike the idea. Infix notation is one of those things I don't like in Scala. It adds less for the programmer / code reader, but it brings ambiguity and complexity. Actually one of the reasons I like about Kotlin is that there a not these possibilities (today).


#40

Christian,

Infix function invocation is possible today for any function with a receiver (instance or extension) and exactly one parameter. See function “to()” as an example. What I’m talking about is limiting the feature to only functions specifically designed to be invoked in the infix form.