Thank you, Adam, for taking the time to write such a well considered response.
I do think this new syntax will be cleaner than what we had previously. My
concerns (which were minor) had to do with the discoverability of features.
Perhaps it is better to sacrifice a small amount of discoverability here to
clean up the most frequently used feature (declaring and configuring tasks).
As several people have pointed out, the creation and configuration of tasks is
so central to using gradle, that a new user will have learned this syntax very
early on in the tutorial and will not need to "discover" it.
Adam Murdoch wrote:
Steve Appling wrote:
I'm still not sure that I understand the motivation to change from
using createTask to the keyword style syntax.
It's a good question. For me, there's a few problems with the old
createTask('name', options) { action } syntax:
- We have been moving towards a declarative approach over an imperative
approach with our DSL. The name 'createTask' does not fit this.
- There is no way to refer to a task using only an identifier, as you
can only declare a task using a string for its name. I think it is
important for the first-class citizens of the model, such as tasks, to
be referenced using a Groovy identifier during their entire lifecycle.
Consistency is important, but also, an identifier is semantically much
stronger than a string. An identifier says 'here is a thing', whereas a
string conveys no real meaning.
- We use a different syntax for declaring tasks, and for declaring all
our other domain objects, such as repositories or configurations. I
think it is important to use the same syntax for declaring all domain
objects, or as similar as we can manage.
- We use a different semantic for declaring tasks, and for declaring all
our other domain objects. When you declare a task, you provide an action
closure, whereas when you declare anything else, you provide a
configuration closure. And when you reference an existing task with a
closure, the closure is treated as a configuration closure.
So, given this, we ended up with:
task name(options-map) { configure-closure } where options-map and
configure-closure are optional.
This addresses the above issues, at the cost of some not-so-common
groovy code. Personally, I think this is worth the trade-off.
Note also that the above syntax transforms naturally into the syntax
for, say, configurations, so that we could allow in the future:
tasks { name(options-map) { configure-closure } }
or
configuration name(options-map) { configure-closure }
I think this looks less like normal groovy code (which makes it
harder to initially understand).
It does look less like normal groovy code. But does it really make it
harder to understand? I'm not so sure. It's the very first thing you hit
in the tutorial, and it's reinforced all the way through the user guide.
It's self describing, and, being consistent with other domain object
declarations, learning this pattern helps you learn the rest of Gradle.
That said, I've left in the option to use something similar to the old
syntax:
task('name', options-map) { configure-closure }
Also, createTask() is still in there as well, though it is deprecated
and will be removed some time after the 0.6 release.
We can try the 2 forms out for a while and see what happens.
I should add that these changes aren't set in stone, yet, though we want
to come up with something reasonable for 0.6. It bothers me a bit that
some people have misgivings. We don't have to use this new syntax, if we
can come up with something that everyone is happy with.
Perhaps I'm just ranting about DSLs in general, which are always a
balance between convenience and ease of learning.
That said, if you are going to use a keyword style syntax, I would
prefer a keyword to create a new task that looked more like a verb -
perhaps createTask or newTask. I think the keyword task is
confusingly close to the Project.task methods and Project.getTasks.
If I was going to the javadoc to try to understand what methods in
project were available to me, I would find this confusing.
task() and getTasks() have been replaced with methods on TaskContainer,
so I don't think that's a problem.
There is already a method to add actions to a task. Why is the "<<"
syntax needed? It just seems like more non-obvious magic. Could you
not just use:
task hello.doLast { stuff to do }
You can do this, if you prefer.
A few reasons for adding the << operator:
- We're just doing what groovy does in this instance. You're adding an
action to a task, so we use the same syntax that groovy uses for adding
things to a collection. In this sense, it's not really non-obvious at
all if you know groovy. It helps you understand by drawing your
attention to the analog with adding things to a collection.
- It's more concise, as this is a very common thing to do.
- doLast and doFirst don't really make sense when you declare a task.
Providing << avoids this to some degree.
I do like the idea of making more of a distinction between action
closures and configuration closures. The 0.5.2 syntax was confusing
initially.
Hans Dockter wrote:
We plan to make another change to the Task DSL before we release 0.6.
At the moment the behavior that if you pass a closure to an existing
task, the closure is used for configuring the task object. If you
pass a closure to a task when creating a task, this closure is used
as an action. The reason for this behavior is that we wanted to make
Gradle behave as convenient as possible for the major use cases. If
you create a simple task like HelloWorld, you usually don't want to
configure it but want to add an action. And if you are accessing a
task provided for example by the Java plugin, you usually want to
configure it. The pay-off for this behavior, is that it can be a bit
confusing at the beginning. And it is different from the normal
Gradle behavior, which always uses closure assigned to objects for
configuring them. The use case that made us change our mind about
what is the best behavior, is when you don't create simple tasks but
tasks of a custom type (e.g. Jar). In such a case you often want to
configure the tasks when you create it (in contrast to simple tasks).
We plan therefore to change the DSL in the following way:
task hello << { <action> }
task hello { <configure> }
task myJar(type: Jar) { <configure> }
existingTask << { <action>} // equivalent to existingTask.doLast
existingTask { <configure> }
We were also thinking about using the work 'do' instead of <<. That
would be nice to read. But this is not trivial to implement in
Groovy. And the << operator is already used in Groovy for adding
elements to a list, which is something similar to adding an action to
a task.
Feedback is very welcome
- Hans
--
Hans Dockter
Gradle Project lead
http://www.gradle.org
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email
--
Steve Appling
Automated Logic Research Team
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email