capturing (or not) mutable local variables

Jesse Sightler jesse.sightler at gmail.com
Fri Nov 19 17:12:08 PST 2010


As a user of the Java language I completely agree that the benefits of local
mutable variable access far outweigh the risks of misuse.
On Nov 19, 2010 7:54 PM, "Neal Gafter" <neal at gafter.com> wrote:
> Vincent-
>
> I for one agree with you wholeheartedly. Designing a language feature
> around one particular category of use case generally results in a poor
> language feature and a need for further extensions. I've written about
this
> phenomenon before <
>
http://gafter.blogspot.com/2006/09/failure-of-imagination-in-language_17.html
>>.
>
> Cheers,
> Neal
>
> On Thu, Nov 18, 2010 at 9:09 AM, Vincent Sevel <vincent at sevel.eu> wrote:
>
>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale
>> behind the decision to not capture mutable local variables.
>> I understand that among several arguments, there is a strong one about
>> preventing people from doing unsafe things. you gave the analogy of
>> the kid, the fence and the pool.
>>
>> if I continue on the analogy, we could say that if the kid disguised
>> himself (eg: final int[] sum = new int[1]) then you would be probably
>> happy, although it did not take much to workaround your fence, and
>> actually people are used to it.
>>
>> of course your fear completely disappears if the variable is of type
>> anything as long as it is final. so you do not have problem with that:
>>
>> class BigGuyThatDoesnotKnowHowToSwim implements Foreachable {
>> int sum = 0;
>>
>> public void do(Object elt) {
>> sum += (Integer) elt;
>> }
>> }
>>
>> final BigGuyThatDoesnotKnowHowToSwim bigguy = new
>> BigGuyThatDoesnotKnowHowToSwim();
>> mylist.foreach(bigguy);
>>
>> really, from the thread safety perspective, the disguised kid and the
>> bigguy are not better than the kid. asking if the guy is a kid is
>> actually the wrong question. you should be asking yourself whether or
>> not the guy actually knows how to swim, assuming he needs to be able
>> to. because you have kids that swim better than you and me, and you
>> have grown up that do not know.
>>
>> this thought leads to another one. in your example you are assuming
>> that the foreach method will get into multithreaded stuff. myself, I
>> did not expect it to be. but if that is the case, truly we want to be
>> careful about it. but should I assume that any method that accepts a
>> block can get into MT? and should I design around that assumption? the
>> basic problem is that you can't figure out what a pool is. so you are
>> saying there are pools out there, let's protect anything that is
>> accessible by kids. so you will protect football fields and
>> playgrounds too.
>>
>> if you are going that route I could argue you could go even further.
>> if multithread safety is the issue, and I cannot rely on the designer
>> to document the limitations of the api, or I cannot rely on the user
>> to use the api the designer had anticipated, we are actually at risk
>> on any method that accepts an argument with state.
>>
>> on that particular issue, it is not about final variables, it is about
>> passing mutable state that supports the contract set by the api. If
>> you had told me that your impl was using MT stuff under the cover,
>> then I would have written a very different block:
>>
>> Object sync = new Object();
>> int sum = 0;
>> list.forEach(#{ e -> synchronized(synch) { sum += e.size(); }});
>>
>> or if I knew there was a pool I would have written a
>> BigGuyThatKnowsHowToSwim.
>>
>> in conclusion your fence is not only overprotecting in cases where we
>> do not need it, but actually it provides protection only in a minority
>> of cases. so really what you want to do if you are not confident that
>> people will read the documentation and take responsibility for their
>> mistakes, you should help them make sure they thought about the issue
>> when we know there is certainty about it.
>>
>> your foreach impl coud say:
>>
>> public void forEach(@Multithreaded Foreachable block) {...}
>>
>> then I would get a warning or an error as a client if I tried that:
>>
>> list.forEach(#{ e -> sum += e.size(); });
>>
>> but the error would disappear if I did:
>>
>> list.forEach(@ThreadSafe #{ e -> synchronized(synch) { sum += e.size();
>> }});
>>
>> the good thing about this is compiler would still complain about the
>> following, and that, in spite of having the final keyword, which is in
>> no way a guarantee of thread safety:
>>
>> final int[] sum = new int[1];
>> list.forEach(#{ e -> sum[0] += e.size(); });
>>
>> I believe that if you do multithread your foreach, you will have not
>> more (or less for that matter) problems with or without capturing
>> mutable variables. because people will write blocks of code that do
>> not access local variables, but still manipulate pojos in a non thread
>> safe way.
>>
>> for that matter, you should probably name your loop method forEachMT.
>>
>> as a final point I would say that the issue is not about mutable
>> variables, it is about carrying clearly the contract constraints to
>> the user of the api. if the spec stays that way, I am hoping that this
>> is not based on the pool paradigm. in the jdk we should learn more
>> from dynamic languages about trust and responsibility taking.
>>
>> regards.
>>
>> vincent
>>
>>
>


More information about the lambda-dev mailing list