Task DSL

View: New views
18 Messages — Rating Filter:   Alert me  

Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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



Re: Task DSL

by Steve Appling :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I'm still not sure that I understand the motivation to change from using
createTask to the keyword style syntax.  I think this looks less like normal
groovy code (which makes it harder to initially understand).  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.

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 }

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
>

--
Steve Appling
Automated Logic Research Team

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Steve,

On May 13, 2009, at 5:10 PM, Steve Appling wrote:

> I'm still not sure that I understand the motivation to change from  
> using createTask to the keyword style syntax.  I think this looks  
> less like normal groovy code (which makes it harder to initially  
> understand).  Perhaps I'm just ranting about DSLs in general, which  
> are always a balance between convenience and ease of learning.

What distinguished those approaches is rather subtle. I can't provide  
any killer argument. But let's look at 0.5.2:

You create as task with createTask("name"). But you can access the  
task with name (unquoted). You could also argue the latter is not  
normal groovy code (and may be you do). The alternative would be to  
say task("name") or with 0.6 tasks.getByName("name"). But I think it  
is nice to access a task like a variable. It is not just convenient  
for the writer, it is also nicer and better to read. One major  
motivation for Adam to change the 0.5.2 behavior was the mismatch  
between using strings for creating task vs. the unquoted name for  
accessing them (Adam, please correct me if I'm wrong). And I think the  
new syntax is therefore more consistent and nicer (better) to read. It  
is kind of funny. Some people (the developers in any case :)) really  
like it. And we were almost surprised that not everybody liked this  
change. But we had our reasons for this and it was not just DSL hype :)

> 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.

We had a discussion a while back about the general approach for the DSL.

See: http://markmail.org/message/uiejxtpqwbsa74sc

Some experienced DSL writers recommended strongly to go for  
declarative vs. describing operations. And I agree with them. Again I  
think this is nicer and better to read.

The Project.task and getTasks method are replaced by the methods of  
the new tasks container (e.g. project.tasks.all/getByName/...). At  
least it is consistent to what we do in configurations/dependencies/
repositories.

> 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 }

Now here I will use the normal groovy code argument :)

List list = []
list << 'String' // normal Groovy code

I think to use doLast you would have to use parentheses:  
task(hello).doLast { ... }

Another argument is: what are the major use cases? If I create a  
custom task, I just want to add an action and I don't care about  
doLast and doFirst. It is rather confusing in this context to use  
doLast and doFirst (and in fact doFirst and doLast has confused quite  
a few people at the beginning). Of course what we could do would be to  
provide an additional method do, which is equivalent to doLast. But  
then we would still have the parentheses problem.

> 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.

I'm happy that there is at least one aspect which you like.

Many thanks for your feedback

- Hans

>
>
> 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
>
> --
> Steve Appling
> Automated Logic Research Team
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>   http://xircles.codehaus.org/manage_email
>
>

--
Hans Dockter
Gradle Project lead
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by Russel Winder-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, 2009-05-13 at 15:36 +0200, Hans Dockter wrote:
[ . . . ]

I have to admit to being ambivalent on the new task definition notation.
On the one hand I prefer it to what was before, on the other hand it is
not a Groovy script per se and so is difficult to read.

If this were just a DSL then clearly AST transforms are a great tool for
handling "parsing on the fly" -- it ensures a language that might
otherwise have to have been an external DSL can be an internal DSL.
However great play is made of Gradle specifications being Groovy
scripts, not scripts in a DSL.  This means a Gradle script should
clearly and obviously be a Groovy program and this means AST transforms
that change standard Groovy rules should not be used.  In this case a
task specification is not obviously Groovy.

On the third hand the issue is trivially that the symbol after the task
"keyword" is being auto stringified.  This is why I find myself totally
unable to decide.  The change from Groovy is trivial and clear, but it
is a change from Groovy and so incomprehensible.

> 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
Using << is already idiomatics in Groovy so no problem there.

Perhaps the example would have been better if the edit
g/existingTask/s//hello/ or g/existingTask/s//myJar/ was applied?

--
Russel.
============================================================
Dr Russel Winder                 Partner

Concertant LLP          t: +44 20 7585 2200, +44 20 7193 9203
41 Buckmaster Road,     f: +44 8700 516 084    voip:  sip:russel.winder@...
London SW11 1EN, UK.    m: +44 7770 465 077    xmpp: russel@...


signature.asc (204 bytes) Download Attachment

Re: Task DSL

by Russel Winder-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Steve,

On Wed, 2009-05-13 at 11:10 -0400, Steve Appling wrote:
> I'm still not sure that I understand the motivation to change from using
> createTask to the keyword style syntax.  I think this looks less like normal
> groovy code (which makes it harder to initially understand).  Perhaps I'm just
> ranting about DSLs in general, which are always a balance between convenience
> and ease of learning.

I agree with you on this one.

> 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.

I have a very different view of language from you on this one.  I prefer
declaration to action so I prefer noun forms to verb forms for this sort
of thing.

> 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 }

<< is traditionally used in C++ and Groovy as an inserter, so append to
a list, etc.  Used in that context it is idiomatic.  Used simply to
replace a call of a method that doesn't have inserter semantics, it is
probably a bad thing to do.

[ . . . ]
--
Russel.
============================================================
Dr Russel Winder                 Partner

Concertant LLP          t: +44 20 7585 2200, +44 20 7193 9203
41 Buckmaster Road,     f: +44 8700 516 084    voip:  sip:russel.winder@...
London SW11 1EN, UK.    m: +44 7770 465 077    xmpp: russel@...


signature.asc (204 bytes) Download Attachment

Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On May 14, 2009, at 8:06 AM, Russel Winder wrote:

<snip>

>> 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 }
>
> << is traditionally used in C++ and Groovy as an inserter, so append  
> to
> a list, etc.  Used in that context it is idiomatic.  Used simply to
> replace a call of a method that doesn't have inserter semantics, it is
> probably a bad thing to do.

Could you explain a bit more what you mean with the last sentence?

- Hans

--
Hans Dockter
Gradle Project lead
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by Russel Winder-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, 2009-05-13 at 20:43 +0200, Hans Dockter wrote:
[ . . . ]

> You create as task with createTask("name"). But you can access the  
> task with name (unquoted). You could also argue the latter is not  
> normal groovy code (and may be you do). The alternative would be to  
> say task("name") or with 0.6 tasks.getByName("name"). But I think it  
> is nice to access a task like a variable. It is not just convenient  
> for the writer, it is also nicer and better to read. One major  
> motivation for Adam to change the 0.5.2 behavior was the mismatch  
> between using strings for creating task vs. the unquoted name for  
> accessing them (Adam, please correct me if I'm wrong). And I think the  
> new syntax is therefore more consistent and nicer (better) to read. It  
> is kind of funny. Some people (the developers in any case :)) really  
> like it. And we were almost surprised that not everybody liked this  
> change. But we had our reasons for this and it was not just DSL hype :)
The core problem here is the use of strings to represent symbols.  (I am
assuming this is why Ruby has symbols separate from strings.)

Using strings as labels for things in a program means that typing (as in
on the keyboard) errors can lead to total confusion.  Using some form of
symbol that the language parser processes means you get an easier
unification of all instances of the symbol, and hence easier error
detection.

I am all for using variables as much as possible as labels, I therefore
really don't like using a string in createTask -- this is why I used a
map for target definition in Gant, but even that is fraught with danger
since it is auto stringified and so actually you loose the error
detection.

So I like the basic idea of a task name being a symbol that the compiler
treats as a variable.  I wonder though if it might be easier just to do:

name = newTask { . . . }

?

> We had a discussion a while back about the general approach for the DSL.
>
> See: http://markmail.org/message/uiejxtpqwbsa74sc
>
> Some experienced DSL writers recommended strongly to go for  
> declarative vs. describing operations. And I agree with them. Again I  
> think this is nicer and better to read.

Steve obviously read things differently, but I'm afraid I still stand by
my declaration not action position.

> The Project.task and getTasks method are replaced by the methods of  
> the new tasks container (e.g. project.tasks.all/getByName/...). At  
> least it is consistent to what we do in configurations/dependencies/
> repositories.

Consistency is always good in these things.

> > 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 }
>
> Now here I will use the normal groovy code argument :)
>
> List list = []
> list << 'String' // normal Groovy code
>
> I think to use doLast you would have to use parentheses:  
> task(hello).doLast { ... }
>
> Another argument is: what are the major use cases? If I create a  
> custom task, I just want to add an action and I don't care about  
> doLast and doFirst. It is rather confusing in this context to use  
> doLast and doFirst (and in fact doFirst and doLast has confused quite  
> a few people at the beginning). Of course what we could do would be to  
> provide an additional method do, which is equivalent to doLast. But  
> then we would still have the parentheses problem.
Another analogy is with hooks in Emacs.  The hook is a thing to which
actions can be appended, it is a list of actions.  the "do first" and
"do last" are just lists of actions so an inserter semantics is exactly
what is happening, and so the traditional idiom of using << applies.

[ . . . ]
--
Russel.
============================================================
Dr Russel Winder                 Partner

Concertant LLP          t: +44 20 7585 2200, +44 20 7193 9203
41 Buckmaster Road,     f: +44 8700 516 084    voip:  sip:russel.winder@...
London SW11 1EN, UK.    m: +44 7770 465 077    xmpp: russel@...


signature.asc (204 bytes) Download Attachment

Re: Task DSL

by Russel Winder-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Thu, 2009-05-14 at 08:10 +0200, Hans Dockter wrote:
[ . . . ]
> > a list, etc.  Used in that context it is idiomatic.  Used simply to
> > replace a call of a method that doesn't have inserter semantics, it is
> > probably a bad thing to do.
>
> Could you explain a bit more what you mean with the last sentence?

The analogy is basically that << should never replace assignment or a
named action other than append if it is to be being used idiomatically.
So

        x = [ ]
        x << a

is fine since the semantics are inserter semantics, the value a is being
appended to x; << replaced append:

        x.append ( a )

If << simply replaces a non appending method call then it is being used
outside its idiomatic meaning and so will lead to misreading of
programs.

doFirst and doLast are appending methods, they append new actions to the
hook.  So << has a natural position somewhere here.  The question is
whether it should replace doFirst or doLast.  The problem is that it
cannot be used with both, it can only be used with one.  The doubt leads
to a cogent argument that perhaps it shouldn't be used with either.

Ambiguity leads to ambivalence :-(

--
Russel.
============================================================
Dr Russel Winder                 Partner

Concertant LLP          t: +44 20 7585 2200, +44 20 7193 9203
41 Buckmaster Road,     f: +44 8700 516 084    voip:  sip:russel.winder@...
London SW11 1EN, UK.    m: +44 7770 465 077    xmpp: russel@...


signature.asc (204 bytes) Download Attachment

Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On May 14, 2009, at 8:22 AM, Russel Winder wrote:

> On Thu, 2009-05-14 at 08:10 +0200, Hans Dockter wrote:
> [ . . . ]
>>> a list, etc.  Used in that context it is idiomatic.  Used simply to
>>> replace a call of a method that doesn't have inserter semantics,  
>>> it is
>>> probably a bad thing to do.
>>
>> Could you explain a bit more what you mean with the last sentence?
>
> The analogy is basically that << should never replace assignment or a
> named action other than append if it is to be being used  
> idiomatically.
> So
>
>        x = [ ]
>        x << a
>
> is fine since the semantics are inserter semantics, the value a is  
> being
> appended to x; << replaced append:
>
>        x.append ( a )
>
> If << simply replaces a non appending method call then it is being  
> used
> outside its idiomatic meaning and so will lead to misreading of
> programs.
>
> doFirst and doLast are appending methods, they append new actions to  
> the
> hook.  So << has a natural position somewhere here.  The question is
> whether it should replace doFirst or doLast.  The problem is that it
> cannot be used with both, it can only be used with one.  The doubt  
> leads
> to a cogent argument that perhaps it shouldn't be used with either.
>
> Ambiguity leads to ambivalence :-(

Thanks for the explanation. Regarding your last point and ambiguity.  
You could say the same about << operator for lists. Lists have a  
couple of methods to get stuff into them (e.g. add(Object) and  
add(index, Object)). But the << operator picks the most common one. We  
do the same.

- Hans

--
Hans Dockter
Gradle Project lead
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On May 14, 2009, at 8:02 AM, Russel Winder wrote:

> On Wed, 2009-05-13 at 15:36 +0200, Hans Dockter wrote:
> [ . . . ]
>
> I have to admit to being ambivalent on the new task definition  
> notation.
> On the one hand I prefer it to what was before, on the other hand it  
> is
> not a Groovy script per se and so is difficult to read.
>
> If this were just a DSL then clearly AST transforms are a great tool  
> for
> handling "parsing on the fly" -- it ensures a language that might
> otherwise have to have been an external DSL can be an internal DSL.
> However great play is made of Gradle specifications being Groovy
> scripts, not scripts in a DSL.  This means a Gradle script should
> clearly and obviously be a Groovy program and this means AST  
> transforms
> that change standard Groovy rules should not be used.  In this case a
> task specification is not obviously Groovy.

Gradle scripts have never been stand alone groovy code (as you of  
course know). We don't claim them to be nor would it give any  
advantage to our users. For 0.7 we plan to merge the functionality of  
settings.gradle into build.gradle and we will use AST transformations  
for this. But there is a fine line we don't want to cross. For me one  
major problem with external DSL is that they are mostly declarative  
and that it is impossible or very hard to use them as a toolset. With  
AST transforms you can create a similar effect. This won't happen to  
the Gradle DSL. All the Gradle elements will play nice with generic  
Groovy logic and statements. Either by using them as they are or by  
providing similar notations for use in statements. We don't want to go  
wild with AST transforms. The less the better. But they have a place  
in our DSL.

<snip>

- Hans

--
Hans Dockter
Gradle Project lead
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by Adam Murdoch-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



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



Re: Task DSL

by Steve Appling :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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



Re: Task DSL

by John Murph :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I still have one reservation about this new syntax.


On Fri, May 15, 2009 at 2:31 AM, Adam Murdoch <a@...> wrote:

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 really like this part.  I like the clean syntax, the configure-closure consistency with other stuff, and I really like the tasks {} syntax suggestion.  All very nice.
 




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.



For me, this is the big problem with the new syntax.  The << operator have such a HUGE importance to meaning, and yet being very non-obtrusive syntactically causes me concern.  Especially if one uses different coding styles.  For instance:

task doSomething {
   configurationIt
   config SomeMore
}

task notAgain << {
   println 'You are not supposed to call me'
}

Especially when you have tasks that are many lines long this is O.K., but not great.  However, we have a coding standard that puts the { on a new line, such that it becomes

task doSomething
{
   configurationIt
   config SomeMore
}

task notAgain <<
{
   println 'You are not supposed to call me'
}

The trailing << on the second task line just seems too easy to overlook, and yet is quite meaningful to the behavior of the script.  This is why I would like (at least an option) for

task notAgain.do
{
   println 'You are not supposed to call me'
}

--
John Murph
Automated Logic Research Team

Re: Task DSL

by Steve Appling :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



John Murph wrote:
> I still have one reservation about this new syntax.
>
>
> On Fri, May 15, 2009 at 2:31 AM, Adam Murdoch <a@...
> <mailto:a@...>> wrote:
>

<clip ...>

>
> For me, this is the big problem with the new syntax.  The << operator
> have such a HUGE importance to meaning, and yet being very non-obtrusive
> syntactically causes me concern.  Especially if one uses different
> coding styles.  For instance:
>
> task doSomething {
>    configurationIt
>    config SomeMore
> }
>
> task notAgain << {
>    println 'You are not supposed to call me'
> }
>
> Especially when you have tasks that are many lines long this is O.K.,
> but not great.  However, we have a coding standard that puts the { on a
> new line, such that it becomes
>
> task doSomething
> {
>    configurationIt
>    config SomeMore
> }
>
> task notAgain <<
> {
>    println 'You are not supposed to call me'
> }
>
> The trailing << on the second task line just seems too easy to overlook,
> and yet is quite meaningful to the behavior of the script.  This is why
> I would like (at least an option) for
>
> task notAgain.do
> {
>    println 'You are not supposed to call me'
> }
>

While I agree that they are confusingly similar, I'm not sure that the .do is
any more obvious than the trailing <<.

I'm not sure of a better solution here.  I have noticed myself making mistakes
while converting our project over to use the new syntax.  Perhaps I'm just slow,
but I have already mixed up task and configuration closures a few times myself.

Previously if you had to do configuration on a newly created task you had to
call configure explicitly like:
    createTask('myTask', type:Copy).configure { config here }

Existing tasks could be configured directly, however:
    myTask { config here }

I don't think this was previously intended to be a feature, but it did
distinguish the use of the closure.

--
Steve Appling
Automated Logic Research Team

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by Steve Appling :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



Steve Appling wrote:
<clip ...>

> While I agree that they are confusingly similar, I'm not sure that the
> .do is any more obvious than the trailing <<.
>
> I'm not sure of a better solution here.  I have noticed myself making
> mistakes while converting our project over to use the new syntax.  
> Perhaps I'm just slow, but I have already mixed up task and
> configuration closures a few times myself.
>
> Previously if you had to do configuration on a newly created task you
> had to call configure explicitly like:
>    createTask('myTask', type:Copy).configure { config here }
>
> Existing tasks could be configured directly, however:
>    myTask { config here }
>
> I don't think this was previously intended to be a feature, but it did
> distinguish the use of the closure.
>

There is actually a problem with this that I had not noticed related to coding
conventions and curly brackets.

previously we used:

    createTask('myCopy', type:Copy).configure
    {
       from file('mysrc')
       into file('mydest')
    }

but this won't work:

    task myCopy(type:Copy)
    {
       from file('mysrc')
       into file('mydest')
    }

This fails with:
Could not compile build file 'C:\test\gradleconfig\build.gradle'.
Cause: startup failed, build_gradle: 8: Ambiguous expression could be a
parameterless closure expression, an isolated open code block, or it may
continue a previous statement;

I can only get the new task syntax for configuration to work if we put the curly
bracket on the same line as the task like:

task myCopy(type:Copy) {
    from file('mysrc')
    into file('mydest')
}

I understand that having the opening bracket on the same line is much more
standard practice now, but we have an 11 year old Java coding standard that
everyone is used to.  I would hate to have our build tool be the only exception
to this.

--
Steve Appling
Automated Logic Research Team

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On May 15, 2009, at 4:34 PM, John Murph wrote:

> I still have one reservation about this new syntax.
>
>
> On Fri, May 15, 2009 at 2:31 AM, Adam Murdoch <a@...>  
> wrote:
>
> 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 really like this part.  I like the clean syntax, the configure-
> closure consistency with other stuff, and I really like the tasks {}  
> syntax suggestion.  All very nice.
>
>
>
>
>
> 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 }

Adam please correct me if I'm wrong, but as far as I can tell the  
syntax above is not easy to implement.

What you can do with current trunk is:

task(hello).doLast
{
    println 'hello'
}

doLast and doFirst is too much detail for creating a simple task with  
an action (where you don't bother about first or last). So let's say  
we introduce (and we might actually do) a do method which delegates to  
doLast. Then you could say:

task(hello).do
{
    println 'hello'
}

The parentheses are required (but no magic).

I personally prefer the:

task hello << {
    println 'hello'
}

It just has less clutter (no parentheses, no dot). We were thinking  
about using the do keyword instead of <<. But considering that this is  
a Groovy DSL, we decided to go for <<, as this is similar to how  
Groovy behaves. And 'do' might become a reserved word in Groovy once  
they introduce do-while. And we had trouble implementing it.

<snip>

- Hans

--
Hans Dockter
Gradle Project Manager
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by hdockter :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On May 15, 2009, at 5:08 PM, Steve Appling wrote:

>
>
> John Murph wrote:
>> I still have one reservation about this new syntax.
>> On Fri, May 15, 2009 at 2:31 AM, Adam Murdoch <a@...  
>> <mailto:a@...>> wrote:
>
> <clip ...>
>
>> For me, this is the big problem with the new syntax.  The <<  
>> operator have such a HUGE importance to meaning, and yet being very  
>> non-obtrusive syntactically causes me concern.  Especially if one  
>> uses different coding styles.  For instance:
>> task doSomething {
>>   configurationIt
>>   config SomeMore
>> }
>> task notAgain << {
>>   println 'You are not supposed to call me'
>> }
>> Especially when you have tasks that are many lines long this is  
>> O.K., but not great.  However, we have a coding standard that puts  
>> the { on a new line, such that it becomes
>> task doSomething
>> {
>>   configurationIt
>>   config SomeMore
>> }
>> task notAgain <<
>> {
>>   println 'You are not supposed to call me'
>> }
>> The trailing << on the second task line just seems too easy to  
>> overlook, and yet is quite meaningful to the behavior of the  
>> script.  This is why I would like (at least an option) for
>> task notAgain.do
>> {
>>   println 'You are not supposed to call me'
>> }

As said in the other mail, you have this option, but with parentheses.  
And right now you have to use doLast. We will discuss whether we  
should introduce a do method delegating to doLast.

>>
>
> While I agree that they are confusingly similar, I'm not sure that  
> the .do is any more obvious than the trailing <<.
>
> I'm not sure of a better solution here.  I have noticed myself  
> making mistakes while converting our project over to use the new  
> syntax.  Perhaps I'm just slow, but I have already mixed up task and  
> configuration closures a few times myself.

On the other hand mixing things up happened to me also pretty  
frequently with the old syntax. Specially when I have created tasks  
with a custom type.

>
>
> Previously if you had to do configuration on a newly created task  
> you had to call configure explicitly like:
>   createTask('myTask', type:Copy).configure { config here }
>
> Existing tasks could be configured directly, however:
>   myTask { config here }
>
> I don't think this was previously intended to be a feature, but it  
> did distinguish the use of the closure.

One thing I like with the new DSL is that we have consistency in  
regard to the syntax for configuring and adding actions. This is no  
longer context dependent. Of course you could do configuration more  
heavyweight to make it more expressive.

With the new syntax you can still do (which is nicer than the above  
IMO):

task(myTask, type: Copy).configure {
    <configure>
}

- Hans

--
Hans Dockter
Gradle Project Manager
http://www.gradle.org





---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Task DSL

by Steve Appling :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



Steve Appling wrote:

<clip ...>

>
> There is actually a problem with this that I had not noticed related to
> coding conventions and curly brackets.
>
> previously we used:
>
>    createTask('myCopy', type:Copy).configure
>    {
>       from file('mysrc')
>       into file('mydest')
>    }
>
> but this won't work:
>
>    task myCopy(type:Copy)
>    {
>       from file('mysrc')
>       into file('mydest')
>    }
>
> This fails with:
> Could not compile build file 'C:\test\gradleconfig\build.gradle'.
> Cause: startup failed, build_gradle: 8: Ambiguous expression could be a
> parameterless closure expression, an isolated open code block, or it may
> continue a previous statement;
>
> I can only get the new task syntax for configuration to work if we put
> the curly bracket on the same line as the task like:
>
> task myCopy(type:Copy) {
>    from file('mysrc')
>    into file('mydest')
> }

As Hans pointed out to me, this will work fine (and be more consistent with how
configuration works elsewhere) if I change my use of parenthesis.

We can use:
    task(myCopy, type:Copy)
    {
       // configure here
    }


--
Steve Appling
Automated Logic Research Team

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email