Effectively final
Brian Goetz
brian.goetz at oracle.com
Fri Aug 19 20:06:36 PDT 2011
Yep, I think reflecting the parallelism in either the name or the type
system is pretty much a requirement. Trying to do "transparent
parallelism" is going to be asking for trouble, just as "transparent
remoting" was in a previous decade. The name-based approaches that you
suggest are a possibility, as is having "parallel views":
collection.asParallel().forEach(block)
which is what PLinQ on .Net does. But the user has to ask for it.
(Scala made a choice similar to .Net.)
Cheers,
-Brian
On 8/19/2011 11:01 PM, Jim Mayer wrote:
> Hi Brian,
>
> Your post prompted a thought about the naming of library methods. Since
> Java has its history and since it is easy to write non-functional code
> (call a method, the evil array hack, etc.), perhaps the methods that are
> intended to support fine grain parallelism should call that out in their
> names.
>
> I'm thinking of 'parallelReduce', ' pReduce', or something like that. I
> suspect that lot of people will mentally model 'reduce' as a shorthand
> for a for loop and will assume sequential evaluation.
>
> Such a naming convention would also help avoid confusion with the
> 'Block' oriented methods that were discussed recently. Finally, it
> would help avoid problems if mutation of locals was allowed in some
> future Java release.
>
> On the downside, the library would be more verbose.
>
> Thanks!
>
> -- Jim
>
> On Aug 18, 2011 12:46 PM, "Brian Goetz" <brian.goetz at oracle.com
> <mailto:brian.goetz at oracle.com>> wrote:
> > This argument might work in another world, but ignores the reality when
> > applied to Java.
> >
> > Yes, there *are* other programming models for concurrency that are less
> > problematic than "everything is mutable and you have to use exclusion
> > and/or partitioning to avoid shooting yourself in the foot."
> >
> > Yes, these models can be implemented in Java, provided that the
> > programmer brings a sufficient degree of discipline to the situation and
> > stays to the unenforced rules of the model. And in some of them,
> > mutable local capture by lambdas would be convenient.
> >
> > Yes, people (myself included) have successfully written large, safe,
> > concurrent systems in Java using this approach.
> >
> > Yes, it is a shame that Java chose the "mutation + locking" route to
> > concurrency.
> >
> > But, if we allowed mutable capture, what is the most common idiom that
> > would use it? It is virtually guaranteed it will be:
> >
> > int sum=0;
> > x.forEach(#{ a -> sum += a.foo(); });
> >
> > And this is not one of those safer models that you hope will emerge into
> > mainstream popularity in the future. This is making Java less safe, by
> > creating a whole new category of serious hazards that didn't exist
> > before -- data races on locals.
> >
> > Given that those safer models have not yet emerged into the mainstream,
> > and this sort of unsafe willy-nilly mutation is definitely here, the
> > tradeoff simply does not add up.
> >
> > I've already indicated we may choose to revisit this in the future,
> > using an approach where the thread-confinement constraint can be
> > enforced by the VM. But we will not be revisiting this now.
> >
> > I applaud your passion, but all of this is well-covered ground, and
> > there's new ground to be covered before we're done. Let's move on.
> >
> >
> > On 8/18/2011 12:00 PM, Tim Fox wrote:
> >> On 18/08/11 16:30, Howard Lovatt wrote:
> >>> On 16/08/2011, at 10:45 PM, Tim Fox<timvolpe at gmail.com
> <mailto:timvolpe at gmail.com>
> >>> <mailto:timvolpe at gmail.com <mailto:timvolpe at gmail.com>>> wrote:
> >>>
> >>>> The point I was making was in response to your assertion that
> >>>> inherently serial code will become less common as time goes on.
> >>>> Actors frameworks (and other hybrid frameworks like node.x) are all
> >>>> based around inherently serial code, and I don't see them dying
> out yet.
> >>>
> >>> I don't get what you are driving at, you have been posting that
> >>> Lambdas should capture mutable local variables and have given Actors
> >>> as an example of why that is necessary.
> >> No, I didn't give actors as an example of why lambdas should mutate
> >> local vars, I gave actors as a counterpoint to your assertion that
> >> serial code will become less common as time came on (Actor code is all
> >> serial and these frameworks are gaining in popularity)
> >>
> >> The point I was trying to make, in a nutshell, was this:
> >>
> >> Conventional wisdom Java concurrency involves letting many threads
> >> execute your objects and using synchronization and what-not to make
> >> those objects threadsafe. This is difficult. Concurrent programming
> is hard.
> >>
> >> There are other ways of making scalable concurrent systems that don't
> >> involve having your code executed by multiple threads concurrently.
> >> (Actor model is one example of this, there are others) This is very
> >> desirable, since if you don't have to worry about making your code
> >> threadsafe, you can open up to a legion of web developers who otherwise
> >> would find concurrency too hard.
> >>
> >> My bet is the latter type of concurrent systems will eventually become
> >> more popular. So... if in this future, developers will be writing
> >> islands of single threaded coded, then it's safe to use lambdas that
> >> mutate local vars in those islands since zero race conditions are
> >> guaranteed.
> >>
> >> So far from being a throwback to the 50s, lambdas which mutate local
> >> vars could be a very useful tool for the next generation of frameworks.
> >> And if Java does not support such lambdas it will be sidelined as a good
> >> programming language for these kinds of frameworks.
> >>
> >> Allowing such mutation would allow you to implement "node.js-like"
> >> frameworks on the JVM. This is already possible in JS/Ruby/Groovy/etc
> >> but is currently very clunky in Java because of lack of lambdas - but
> >> even with Java 8 proposed lambdas it would still be clunky because of
> >> the effectively-final rule.
> >>
> >>
> >>> What I demonstrated, was that for Actors you wouldn't use Lambdas but
> >>> classes and therefore your argument that Lambdas should capture local
> >>> mutables because of Actors is non sequitur.
> >>>
> >>> Now you seem to be saying that you were talking about particular
> >>> styles of code. Are you arguing about capturing mutables or not?
> >>>
> >>> PS Capturing a mutable variable in an Actor and passing it via message
> >>> to another actor is problematic. Again indicating that Lambdas should
> >>> not capture mutable state.
> >>>
> >>> PPS Also note that Scala has introduced map/reduce style collections
> >>> (presumably because people prefer these to Actors - which it already
> >>> has). Again map/reduce is best suited to not capturing mutable
> variables.
> >>>
> >>>> Quite the opposite, I see them getting more popular :)
> >>>>
> >>>> On 16/08/11 16:25, Howard Lovatt wrote:
> >>>>> I don't see that the actor or FJ type frameworks are effected much,
> >>>>> because the messages are immutable, e.g. a standard actor example:
> >>>>>
> >>>>> http://java.dzone.com/articles/scala-threadless-concurrent
> >>>>>
> >>>>> Recoded in Java would be something like this:
> >>>>>
> >>>>> public class Actor extends FJTask {
> >>>>> private Queue messages ...
> >>>>> public void accept( final Object message ) ...
> >>>>> public Object acceptAndReply( final Object message ) ...
> >>>>> public Future<Object> acceptAndReplyLater( final Object message ) ...
> >>>>> protected void reply( final Object response ) ...
> >>>>> protected Object getMessage() ...
> >>>>> }
> >>>>>
> >>>>> |10.||public class Accumulator ||extends| |Actor {|
> >>>>> |11.||public void run() {|
> >>>>> |12.||int sum = ||0;|
> >>>>> |13.||for(;;) {|
> >>>>> |14.|final Object message = getMessage();
> >>>>> |15.|if ( message instanceof |Accumulate ) { || sum +=
> >>>>> ((Accumulate)message).n; ||}|
> >>>>> |16.|else if ( message instanceof |Reset ) { sum = ||0; }|
> >>>>> |17.|else if ( message instanceof Total ) { reply(sum); break;
> >>>>> |18.||}|
> >>>>> |19.||}|
> >>>>> |20.||}|
> >>>>> |21.||}|
> >>>>>
> >>>>>
> >>>>> The lambdas might prove useful for the messages, that are immutable
> >>>>> anyway, but the actors themselves would always be instances of a
> >>>>> class because you need to be able to create more actors as the
> >>>>> program runs and you can't do this with a lambda (or a closure many
> >>>>> other language). As I said at the beginning I don't see that an
> >>>>> Actor framework would change much one way or the other if Lambdas
> >>>>> could mutate a variable or not.
> >>>>>
> >>>>> Cheers,
> >>>>>
> >>>>> -- Howard.
> >>>>>
> >>>>> Sent from my iPad
> >>>>>
> >>>>> On 16/08/2011, at 6:57 PM, Tim Fox<timvolpe at gmail.com
> <mailto:timvolpe at gmail.com>
> >>>>> <mailto:timvolpe at gmail.com <mailto:timvolpe at gmail.com>>> wrote:
> >>>>>
> >>>>>> On 16/08/11 12:36, Howard Lovatt wrote:
> >>>>>>> I think the correct decision has been made, inherently serial code
> >>>>>>> will become less common as time goes on.
> >>>>>> The growing popularity of actor model implementations (actors of
> >>>>>> course
> >>>>>> only allow serial execution of code in the actor), e.g. Erlang and
> >>>>>> Akka,
> >>>>>> and the growing popularity of languages which are inherently serial
> >>>>>> (e.g. JavaScript) are a clear counterpoint to your argument.
> Also look
> >>>>>> at webworkers, node.js. I could go on.
> >>>>>>
> >>>>>> I'd argue the current trend is quite opposite to what you see. Non
> >>>>>> serial code will become unusual (because concurrency is hard), and
> >>>>>> serial code will become the norm (because it's easier to code,
> >>>>>> especially for the web developer masses). Systems will scale by
> having
> >>>>>> multiple "islands" of serial code (e.g. multiple actors in the
> >>>>>> actor model).
> >>>>>>
> >>>>>> Please note that just because all code is executed serially does not
> >>>>>> mean you can't exploit parallelism. You can do fork/join type
> stuff by
> >>>>>> sending messages to other "islands" and reassembling the results as
> >>>>>> they
> >>>>>> come back.
> >>>>>>> Therefore optimizing new features for parallel execution is the
> >>>>>>> correct path to take and also constant with Java's past of taking
> >>>>>>> existing concepts into the mainstream. When Java was introduced
> >>>>>>> the concentration on objects and garbage collection was not
> >>>>>>> mainstream and was opposed by many. I think the same will happen
> >>>>>>> with mutable data and serial execution and it is nice that Java is
> >>>>>>> once again daring to be different and adhering to the mantra that
> >>>>>>> less is more.
> >>>>>>>
> >>>>>>> Cheers,
> >>>>>>>
> >>>>>>> -- Howard.
> >>>>>>>
> >>>>>>> Sent from my iPad
> >>>>>>>
> >>>>>>> On 16/08/2011, at 4:56 PM, Steven Simpson<ss at comp.lancs.ac.uk
> <mailto:ss at comp.lancs.ac.uk>
> >>>>>>> <mailto:ss at comp.lancs.ac.uk <mailto:ss at comp.lancs.ac.uk>>> wrote:
> >>>>>>>
> >>>>>>>> On 16/08/11 01:51, Stephen Colebourne wrote:
> >>>>>>>>
> >>>>>>>> [snip: lots of syntax options for permitting mutable locals]
> >>>>>>>>> int #total = 0;
> >>>>>>>>> list.apply(#{item -> total += item});
> >>>>>>>>>
> >>>>>>>>> ie. a way to introduce a local variable that can be managed
> safely.
> >>>>>>>> For the simple example given, you could translate 'total' into an
> >>>>>>>> AtomicInteger, but if there are other variables to be
> accessed, you'd
> >>>>>>>> have to box them together, and make the whole lambda synchronize
> >>>>>>>> on it,
> >>>>>>>> or at least from the first use of the box to the last. Trying to
> >>>>>>>> patch
> >>>>>>>> the call site like this doesn't seem to be particularly
> >>>>>>>> promising. The
> >>>>>>>> alternative is to require List.apply to make additional
> >>>>>>>> guarantees about
> >>>>>>>> how it executes the lambda.
> >>>>>>>>
> >>>>>>>> Would it not be better to let List.apply get on with its potential
> >>>>>>>> parallelism, and define other methods that do make extra
> guarantees,
> >>>>>>>> e.g. that the lambda will be executed serially, or even on the
> >>>>>>>> caller's
> >>>>>>>> thread?
> >>>>>>>>
> >>>>>>>> Tim's cases include, for example, setTimeout(int, Runnable),
> >>>>>>>> which must
> >>>>>>>> make such a guarantee, if only informally in its documentation.
> >>>>>>>> To be
> >>>>>>>> more formal:
> >>>>>>>>
> >>>>>>>> * Declare setTimeout(int, @Callback Runnable).
> >>>>>>>> * When a lambda is assigned to a @Callback Runnable, allow the
> >>>>>>>> lambda body to mutate locals (without error or warning).
> >>>>>>>> * Don't permit a @Callback Runnable (which is tainted) to be
> >>>>>>>> assigned to a plain Runnable (without error or warning).
> >>>>>>>>
> >>>>>>>> This way, the likes of List.apply don't have to make any
> guarantees,
> >>>>>>>> requiring the caller to make corresponding ones (automatically
> >>>>>>>> achieved
> >>>>>>>> by not being generally allowed to mutate locals). Meanwhile,
> >>>>>>>> setTimout
> >>>>>>>> makes additional guarantees, to the convenience of the caller,
> who is
> >>>>>>>> specially permitted to mutate locals.
> >>>>>>>>
> >>>>>>>> Cheers,
> >>>>>>>>
> >>>>>>>> Steven
> >>>>>>>>
> >>>>>>
> >>>>>>
> >>>>
> >>
> >>
> >
More information about the lambda-dev
mailing list