Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id D9EDA200ACC for ; Mon, 2 May 2016 16:57:01 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id D89C31609B0; Mon, 2 May 2016 16:57:01 +0200 (CEST) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 06D0F1609A6 for ; Mon, 2 May 2016 16:57:00 +0200 (CEST) Received: (qmail 90657 invoked by uid 500); 2 May 2016 14:57:00 -0000 Mailing-List: contact dev-help@groovy.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@groovy.apache.org Delivered-To: mailing list dev@groovy.apache.org Received: (qmail 90647 invoked by uid 99); 2 May 2016 14:56:59 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 02 May 2016 14:56:59 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id 7A64F180290 for ; Mon, 2 May 2016 14:56:59 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.179 X-Spam-Level: * X-Spam-Status: No, score=1.179 tagged_above=-999 required=6.31 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, HTML_MESSAGE=2, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, SPF_PASS=-0.001] autolearn=disabled Authentication-Results: spamd3-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com Received: from mx2-lw-us.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id zh_VUEY0BxLB for ; Mon, 2 May 2016 14:56:57 +0000 (UTC) Received: from mail-wm0-f47.google.com (mail-wm0-f47.google.com [74.125.82.47]) by mx2-lw-us.apache.org (ASF Mail Server at mx2-lw-us.apache.org) with ESMTPS id 57C465F60E for ; Mon, 2 May 2016 14:56:56 +0000 (UTC) Received: by mail-wm0-f47.google.com with SMTP id g17so146770555wme.1 for ; Mon, 02 May 2016 07:56:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to; bh=WucluMODERhottIfgAa/NVEYQRUYzhyQabUDvsgUX1Q=; b=TfdTm67hUR9uy904uKi7u/dw7B3xNgPH6SUxJtZJ6hnAA+02MT6xliBpUzkXa/GVXE yTKIQ92heQPtWiatuPco2qfD2qP4qVikNHWNdFLCInZJ4uXx3n059N+rizkz9EEuifmb p/wYOZ4BGy5u6+pLCm2s7CI7+X0PaDdz4aIqKobIZwdzI37ZcH1KEwDUApHvMvNh3Q/h J3/2Qc5sGWqcT5ZSswtLMYeST95eFg1bX4F4y4MDyQ4tQrrfefr9QM14+l5VjWcyQCaZ ILiBbIJrU2JzUW5qSi7eizOBmtzA1TTPxbpF7V0T/YvXYtz2FcpHviNTBwfb1OKKqcv4 vq7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to; bh=WucluMODERhottIfgAa/NVEYQRUYzhyQabUDvsgUX1Q=; b=kW0wstOwlUtPbnGuUYZl6W8l1CAvDlbvCtR/WR9e/i3RSVnbWeiTJaeo5jF6gMKXzc +NmahSW/akcFgVqkY3mRaFE2fj9WXONcBa6N1VAx7V2zH1BIdSbaNW+yIuoC1yNjWnId 0Z8SV65tqtLORJ41j/wwHlvpEF0RPtTPgIap8gz+7UWenCI9Gb/wu3R9l3gIYLkLvTTi 7+5tW7CKRd7MadhhiJq7tBSHlerw403WoseoBPvZ7mGJZYKyiFhoUa16VVpslHSO/POr 77YUIByjkIEQMgMVrwDEbSe52n9xAc09mGoglDSpx6209OUpcNGKLxrBBamuVOC3JHeQ wAAA== X-Gm-Message-State: AOPr4FX7hAo42P8ZIossFjLBru8Wo2QSThy2TQPfGH4b7SLKvcUbKCBaoRZgytAMNa9Pxfx4cVqocvExJ/dF1g== MIME-Version: 1.0 X-Received: by 10.28.148.149 with SMTP id w143mr19371708wmd.10.1462201014905; Mon, 02 May 2016 07:56:54 -0700 (PDT) Received: by 10.194.29.10 with HTTP; Mon, 2 May 2016 07:56:54 -0700 (PDT) In-Reply-To: References: Date: Mon, 2 May 2016 16:56:54 +0200 Message-ID: Subject: Re: Automatic closure coercion and delegate From: Guillaume Laforge To: dev@groovy.apache.org Content-Type: multipart/alternative; boundary=001a114b82701572a60531dd3482 archived-at: Mon, 02 May 2016 14:57:02 -0000 --001a114b82701572a60531dd3482 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable +1 On Mon, May 2, 2016 at 4:44 PM, C=C3=A9dric Champeau 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 { > void execute(T object) > } > > class Person { > String name > } > > void configure(Action config) { > config.execute(person) > } > > then, you can call it in Groovy like this: > > configure { > it.name =3D 'Bob' > } > > Whereas if we had a closure version, a nice and idiomatic way would be to > write: > > configure { > name =3D '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 sour= ce > files. Typically, we don't have access to the JDK sources, so we have to > write: > > def max =3D['Cedric','Jochen','Guillaume', 'Paul'].stream() > .mapToInt { it.length() } > .max() > .orElse(0) > > Where with this strategy we could use: > > def max =3D['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` interface above. Those actions allow the user to > configure the domain objects typically from plugins written in Java (wher= e > 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 stati= c > compilation: then, you have no choice but implementing the `Closure` > versions. This might be an option for Gradle (even though it would be ver= y > 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... > > --=20 Guillaume Laforge Apache Groovy committer & PMC Vice-President Product Ninja & Advocate at Restlet Blog: http://glaforge.appspot.com/ Social: @glaforge / Google+ --001a114b82701572a60531dd3482 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
+1

On Mon, May 2, 2016 at 4:44 PM, C=C3=A9dric Champeau <cedr= ic.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 S= AM type:

interface Action<T> {
=C2= =A0 =C2=A0void execute(T object)
}

class= Person {
=C2=A0 =C2=A0 String name
}

void configure(Action<Person> config) {=C2=A0
=C2= =A0 =C2=A0config.execute(person)
}

then,= you can call it in Groovy like this:

configure {<= /div>
=C2=A0 =C2=A0it.name= =3D 'Bob'
}

Whereas if we h= ad a closure version, a nice and idiomatic way would be to write:

configure {
=C2=A0 =C2=A0name =3D 'Bob'
}

Note that in the `Action` version, we h= ave to prefix everything with "it.".

My = wish is to make automatic closure coercion automatically set the delegate t= o the first argument, if available, and the delegation strategy to delegate= first.

Basically, it is important to integrate wi= th Java 8 style SAM types and still benefit from a nicer Groovy DSL _withou= t_ having to change the source files. Typically, we don't have access t= o the JDK sources, so we have to write:

def m= ax =3D['Cedric','Jochen','Guillaume', 'Paul'= ;].stream()
=C2=A0 .mapToInt { it.length() }
=C2=A0 .ma= x()
=C2=A0 .orElse(0)
=C2=A0=C2=A0
Wher= e with this strategy we could use:

def max = =3D['Cedric','Jochen','Guillaume', 'Paul'].= stream()
=C2=A0 .mapToInt { length() }
=C2=A0 .max()
=C2=A0 .orElse(0)
=C2=A0=C2=A0
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 `Act= ion<T>` interface above. Those actions allow the user to configure th= e domain objects typically from plugins written in Java (where you cannot u= se a closure). Since the `Closure` equivalent methods are always the same a= nd that it's super simple to forget to implement one, Gradle chose to _= not_ implement the `Closure` versions. Instead, they are generated at runti= me, 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 i= mplementing the `Closure` versions. This might be an option for Gradle (eve= n 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 take= s all java classes and generates an extension class with closure versions f= or all, also, but I'd like to know first what you think of this idea...=




--
Guillaume Laforge
Apache Groo= vy committer & PMC Vice-President
Product Ninja &= amp; Advocate at Restlet

--001a114b82701572a60531dd3482--