groovy-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From C├ędric Champeau <cedric.champ...@gmail.com>
Subject Automatic closure coercion and delegate
Date Mon, 02 May 2016 14:44:51 GMT
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...

Mime
View raw message