groovy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Joe Wolf <joew...@gmail.com>
Subject Re: Automatic closure coercion and delegate
Date Mon, 02 May 2016 15:25:45 GMT
+1

Would it be sensible/possible to add a Closure.FIRST_ARGUMENT resolve
strategy and include it in the default resolution chain? The 'it'-less
closure would behave as expected even without pre-assigning the delegate
(provided that length() was not defined by the delegate/owner). It'd still
probably be a good idea to automatically set the delegate anyways...just
throwing out some thoughts.

[Hi, all. This is my first post to the list--been a happy Groovy user since
version 1.5]

-Joe


On Mon, May 2, 2016 at 10:56 AM, Guillaume Laforge <glaforge@gmail.com>
wrote:

> +1
>
> On Mon, May 2, 2016 at 4:44 PM, C├ędric Champeau <cedric.champeau@gmail.com
> > wrote:
>
>> Hi guys,
>>
>> I've been grumpy about this for a bit too long to keep it for myself, so
>> let me explain the issue :)
>>
>> Imagine you have a Java method that accepts a SAM type:
>>
>> interface Action<T> {
>>    void execute(T object)
>> }
>>
>> class Person {
>>     String name
>> }
>>
>> void configure(Action<Person> config) {
>>    config.execute(person)
>> }
>>
>> then, you can call it in Groovy like this:
>>
>> configure {
>>    it.name = 'Bob'
>> }
>>
>> Whereas if we had a closure version, a nice and idiomatic way would be to
>> write:
>>
>> configure {
>>    name = 'Bob'
>> }
>>
>> Note that in the `Action` version, we have to prefix everything with
>> "it.".
>>
>> My wish is to make automatic closure coercion automatically set the
>> delegate to the first argument, if available, and the delegation strategy
>> to delegate first.
>>
>> Basically, it is important to integrate with Java 8 style SAM types and
>> still benefit from a nicer Groovy DSL _without_ having to change the source
>> files. Typically, we don't have access to the JDK sources, so we have to
>> write:
>>
>> def max =['Cedric','Jochen','Guillaume', 'Paul'].stream()
>>   .mapToInt { it.length() }
>>   .max()
>>   .orElse(0)
>>
>> Where with this strategy we could use:
>>
>> def max =['Cedric','Jochen','Guillaume', 'Paul'].stream()
>>   .mapToInt { length() }
>>   .max()
>>   .orElse(0)
>>
>> Of course, it may look a bit superficial but it is super important for
>> nice DSLs like in Gradle. Typically, Gradle has a lot of domain objects
>> that use the `Action<T>` interface above. Those actions allow the user to
>> configure the domain objects typically from plugins written in Java (where
>> you cannot use a closure). Since the `Closure` equivalent methods are
>> always the same and that it's super simple to forget to implement one,
>> Gradle chose to _not_ implement the `Closure` versions. Instead, they are
>> generated at runtime, so the objects are decorated with one `Closure`
>> method for each `Action` one.
>>
>> Unfortunately, this approach is defeated as soon as you want to use
>> static compilation: then, you have no choice but implementing the `Closure`
>> versions. This might be an option for Gradle (even though it would be very
>> tedious), but not for all cases (we could also do this using extension
>> methods, though, but really, you'd be doing this for _all_ domain objects).
>> I think I could write a code generator that takes all java classes and
>> generates an extension class with closure versions for all, also, but I'd
>> like to know first what you think of this idea...
>>
>>
>
>
> --
> Guillaume Laforge
> Apache Groovy committer & PMC Vice-President
> Product Ninja & Advocate at Restlet <http://restlet.com>
>
> Blog: http://glaforge.appspot.com/
> Social: @glaforge <http://twitter.com/glaforge> / Google+
> <https://plus.google.com/u/0/114130972232398734985/posts>
>

Mime
View raw message