Re: some things about Closure

Jochen Theodorou <[email protected]> Wed, 15 Jul 2026 13:46:04 +0200
Newsgroups gmane.comp.lang.groovy.devel
Message-ID <[email protected]>
On 7/15/26 08:39, Paul King wrote:
> AI read below - I will push for further free names support.
>=20
> ----
>=20
> You're right and my sentence was sloppy. The real split is:
>=20
>    * Closure with no free names (the { x -> x+1 } class): provably
> delegate-independent
>      by inspection. Types irrelevant.
>    * Closure WITH a free name (foo(), or a bare bar): you have to know
> whether that name
>      resolves to the owner (safe to bind at compile time) or could be a
> delegate member
>      (unsafe). @CompileStatic's type checker resolves each one and
> records where it landed
>      (the IMPLICIT_RECEIVER path) =E2=80=94 so it can *prove* the answer
> automatically. Dynamic
>      compilation doesn't resolve those names at compile time, so it
> cannot prove it =E2=80=94 which
>      is why the dynamic path uses the @PackedClosures trust assertion
> plus the runtime
>      guard for that subset.
>=20
> That is the actual role of types: they let the compiler auto-*prove*
> soundness for the
> free-name subset. For the trivial subset there is nothing to prove.

Let me express it like this:

(A) variables
(A1) no free variables like  { x -> x+1 }. Easy, already mentioned
(A2) x is from lexical scope of method. {x+1}, where x is a local=20
variable or parameter. Here Closure already uses its reference based=20
logic. These are also possible PackedClosure candidates.
(A3) x is from field or property. Here only static compilation=20
guarantees that the name "x" actually means the field or property and=20
will not be intercepted or redirected by some MetaClass or Closure strateg=
y
(A4) x is not associated with a field or property, nor from the lexical=20
scope of the method. x is then a truly free variable that gets its=20
meaning only by setting a Closure strategy or by manipulating meta=20
classes. If this is used as method argument, we can in static=20
compilation still see if the matching method provides a delegatesTo or=20
something like that to give the free variable a meaning after all.=20
Otherwise it will fail compilation in static mode. Still works in=20
dynamic mode. But yes, here only static compilation can find if that is=20
a possible PackedClosure. But then it would still have to be called=20
differently to actually work.

For case A4 it would mean that the PackedClosure still could work, but=20
needs the free variables as input. That deviates from the Closure idea=20
and makes it more like a template - meaning the dynamic part is kept out=
=20
of the construct and becomes part of the delegate or it becomes directly=
=20
part of the call/doCall method. It is a question of how far we want to=20
go here.

But imagine we can split a Closure into something that binds the lexical=
=20
names directly and then has a mechanism to deal with the free names=20
only, then we have a concept Closure is actually using already today,=20
but maybe we can migrate that concept and make the MOP part for only the=
=20
resolution of the free variables. Then there would be only a single=20
delegate, now owner and this, and no resolution strategy. All that would=
=20
move out of the Closure. This new Closure could then become the core for=
=20
the old Closure maybe. And maybe we would have a compiler option that=20
will switch from the old to the new Closure... just ideas

(B) methods
Besides free names for variables/fields/properties/dynamic, there are=20
also unqualified method calls. Here we have less variants
(B1) dynamic mode: a method call is resolved according to the strategy=20
and MOP, no direct binding. Example {foo()}, here foo is resolved=20
dynamically and the meta class system goes to quite some lengths to try=20
to resolve it.
(B2) static mode: foo is resolved by the compiler, no MOP involved.=20
PackedClosure could be possible. "this" is captured I think already,=20
which means the more advanced cases where the parameter annotation=20
determines the target of such a method call is maybe still open. Again=20
this would probably require a delegate and thus maybe a different way to=
=20
make the call. Similar to A4.


To sum it up. My ambition is to make something like PackedClosure the=20
core of modern Groovy. With a split of a static part and a dynamic part,=
=20
with only one instance per Closure and no inner classes at all. With=20
only one delegate, that is something like an MOP adapter, which takes=20
care of the dynamic part. In case of static compilation that is empty.=20
How exactly that MOP adapter looks like is not fully clear to me yet. I=20
needs the relevant dynamic data and the metaclass. Just to give a=20
temporary name let me call it OpenClosure (which is a bad name since it=20
is kind of a contraction). A classic Closure would then consist of the=20
OpenClosure and the adapter and inside reroute the calls to them. A=20
PackedClosure would be almost identical to OpenClosure and can probably=20
be replaced by it.

bye Jochen