St.Antario
St.Antario

Reputation: 27435

Groovy function call omiting the parentheses

According to the gradle documentation/section 13.5.2 we can omit parentheses in a method call:

Parentheses are optional for method calls.

But it seems it doesn't work when we try to apply the java plugin. If a script contains the following line:

apply [plugin: 'java']

We'll get the error:

 Maybe something should be set in parentheses or a comma is missing?
 @ line 1, column 8.
     apply [plugin: 'java']
            ^

But if we put this Map-literal into a parentheses it'll work fine.

apply([plugin: 'java'])

So we can't omit the parentheses when the argument is a Map, can we?

Upvotes: 11

Views: 4172

Answers (2)

SirWolf2018
SirWolf2018

Reputation: 21

While it's true that the usual syntax of an array or map in Groovy uses brackets (for example, for empty ones you typically write [] or [:], respectively), the same bracket symbols are interpreted as the index operator if it follows an identifier. Then Groovy tries to interpret apply as a property of the Gradle project, even if it does not exist. Groovy, as a dynamic language, allows us to define properties dynamically, so there is not always a way to tell at compile time if a property is going to exist. While it is questionable if that is a good design or not, Gradle's ExtraPropertiesExtension very much makes use of this dynamic nature in Groovy for convenience. If you prefer more strict typing, I suggest you try the Kotlin DSL, which has much less of this kind of problems (I do not think it is completely gone, as we can still explicitly declare variables as dynamic type).

On the other hand, one purpose of a DSL is to be concise and remove useless ceremony. That is one thing Groovy excels at, because if the only parameter you need to pass to a method or closure is just an array or a map, you can just omit all kinds of brackets: apply plugin: 'java'. (Anything more complex than that is questionable for a DSL.)

And that is why I think that adding parentheses all the time (e.g., apply([plugin: 'java'])) to everywhere is not the right approach to build scripts whose code is supposed to look declarative and to support that, very DSL like, at least not if you use Groovy, which was designed exactly for that purpose. Never using a language feature, even where it might have an advantage, is what lead to books like Douglas Crockford's JavaScript: The Good Parts, where the author recommends we always use semicolons, and that is one of the most argued practices on the Internet, e.g. disagreed by the JavaScript Standard Style. I personally think that it is more important to know the language you work with and e.g., know when a semicolon is needed, in general to produce the best quality code. The semicolon example may admittedly not be such a strong example for other than reducing ceremony. But just because a language feature can be misused, it does not mean we should ban its use. A kitchen knife is not bad just because we can hurt people with it. And the same can be told about Kotlin's most judged features.

Although nowadays we got the plugins DSL, which is the preferred way of loading plugins (even if plugins are put in buildSrc, which will just need some metadata definition), this question may still be relevant for the following reasons:

  • programmatically applying plugins, e.g. subprojects { apply plugin: 'java' },
  • including custom build scripts using a similar syntax: apply from: 'myscript.gradle',
  • and maybe also if for some reason you can only refer to a plugin by its class name.

Therefore the question is still valid.

Upvotes: 2

BarrySW19
BarrySW19

Reputation: 3819

As the specification says, parentheses can be omitted when there is no ambiguity. I suspect the ambiguity in this case arises because the statement without parentheses looks a lot like array index syntax and the parser has trouble working out whether you are calling a method named 'apply' or trying to do something with an array named 'apply'.

Personally, this is why I tend to always use parentheses - if the parser can't work it out I'm sure another programmer reading the code won't either.

Upvotes: 17

Related Questions