Explicit captures in lambda expression
Attila Kelemen
attila.kelemen85 at gmail.com
Tue Jan 7 12:31:43 UTC 2025
I like this idea, because I run into bugs of accidentally capturing "this"
so many times, and the least of the problems when this causes a crash (like
serialization issues), but sometimes it "just" creates a potentially huge
unwanted memory retention. The workaround of moving the lambda declaration
into a separate static method is very-very tedious.
I do not however care so much about non-final capture (I'm not sure if it
should even be allowed). What - in my opinion - the lambda capture rule
should be is that the variable can be captured if it is definitely assigned
before the lambda, and definitely not assigned after the lambda.
Also, I don't think `[t = x]` is needed, I think that part just complicates
the language with very little gain. Just allowing an explicit variable name
capture list should be enough.
Attila
Quân Anh Mai <anhmdq at gmail.com> ezt írta (időpont: 2025. jan. 7., K, 7:17):
> To add more context regarding JDK-8347047, even without the core lib bug,
> the method is easily misused. The method in consideration looks like this:
>
> MemorySegment MemorySegment::reinterpret(long size,
> Consumer<MemorySegment> cleanup)
>
> `cleanup` is triggered when the returned segment is closed, which for some
> kind of MemorySegment, is at the time the old MemorySegment instance (this)
> and the new MemorySegment instance (the returned segment) are
> garbage-collected. As a result, `cleanup` must not reference the old
> segment instance. It is given a freshly created MemorySegment instance and
> must use it. However, it is easy to mistakenly use the old instance instead:
>
> MemorySegment oldMs;
> oldMs.reinterpret(size, ms -> free(oldMs));
>
> While it should be:
>
> oldMs.reinterpret(size, ms -> free(ms));
>
> With explicit captures, it is easy to see if oldMs is mistakenly captured
> as this would not compiled:
>
> oldMs.reinterpret(size, [](ms) -> free(oldMs));
>
> Cheers,
> Quan Anh
>
> On Tue, 7 Jan 2025 at 12:30, Quân Anh Mai <anhmdq at gmail.com> wrote:
>
>> Hi,
>>
>> A feature I missed from C++ is explicit captures in lambda expressions
>> and from recent events I think that it may be a valuable feature to have in
>> Java.
>>
>> JEP-8341785[1] proposes treating loop variables as final inside the loop
>> body, the main motivation is to allow their usage in lambda expressions
>> inside the loop body. However, it seems to be withdrawn due to the concern
>> of making an exception. I believe explicit captures would remove the
>> condition that the variable being captured needs to be effectively final,
>> resolving the motivation of JEP-8341785 and many other cases.
>>
>> JDK-8347047[2] is a bug caused by the implicit capture of the this
>> reference inside a lambda, which leads to the object never being reclaimed
>> by the garbage collector, the thing we actually want to capture there is
>> the result of the method call this.address(). In this case, implicit
>> captures is very error-prone and it would be better if we can explicitly
>> declare what we want to capture instead to avoid accidentally capturing
>> unwanted variables.
>>
>> As a result, I propose that:
>>
>> - A lambda expression can optionally have a captures, if that is the
>> case, all the variables being captured by the lambda expression need to be
>> declared inside it.
>> - We can capture an effectively final variable without giving it another
>> name, but for the others, to avoid confusion, another name for the variable
>> is needed.
>>
>> E.g, suppose a, b are effectively final variables and x, y are not:
>>
>> () -> a; // Allowed, implicit capture of a
>> []() -> a; // Disallowed, a is not captured and hence not defined here
>> [a]() -> a; // Allowed, explicit capture of a
>> [a, b]() -> a + b; // Allowed, capture 2 variables
>> () -> x; // Disallowed, cannot implicitly capture non-effectively-final
>> variable
>> [x]() -> x; // Disallowed, capture a non-effectively-final without
>> reassignment
>> [t = x]() -> x; // Disallowed, x is not defined here
>> [t = x]() -> t; // Allowed, x is captured and assigned to the variable t
>> inside the lambda
>> [t = this.address()]() -> t; // Maybe?
>>
>> Cheers,
>> Quan Anh
>>
>> [1]: https://openjdk.org/jeps/8341785
>> [2]: https://bugs.openjdk.org/projects/JDK/issues/JDK-8347047
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20250107/cdec15d4/attachment.htm>
More information about the amber-dev
mailing list