Checked exceptions within Block<T>

Neal Gafter neal at gafter.com
Tue Jan 15 17:11:28 PST 2013


I have a few points for you.

   - The kinds of guarantees you're contemplating are difficult to encode
   in the language.  You haven't given me any hint as to how you would encode
   it into language rules, and neither does the message you reference in a
   link.
   - The rules you appear to contemplate and far stronger than they need to
   be to be as useful as you imagine.  There are many useful things that one
   can do using threads that would not violate any of the requirements that
   you imagine would be useful.
   - The most important guarantee that you appear to contemplate is that a
   closure does not outlive its enclosing control context.  But that guarantee
   is not nearly as useful as guarantees about other things that we do not
   guarantee, such as stack overflow and null references.  Languages with
   these kind of constructs do not as a practical matter suffer problems with
   closure lifetime extending past than enclosing control context, and they
   are easily handled as runtime exceptions.  In short, I don't think the
   problem you've not solved is not worth solving.
   - The transparencies that are valuable for lambdas are independent from
   and orthogonal to whether the lambda is used in the same thread in which it
   was created, or whether it is used concurrently.

Cheers,
Neal

On Tue, Jan 15, 2013 at 4:24 PM, Steven Simpson <ss at comp.lancs.ac.uk> wrote:

> Hi Neal - did you mean to reply off-list?  If not, feel free to repost as
> if nothing had happened, and I'll do likewise, or just reply back on the
> list to the message below, as you see fit.
>
>
> On 15/01/13 22:12, Neal Gafter wrote:
>
>> What is a serial guarantee, and how do you imagine encoding it into the
>> language rules?
>>
>
> Short answer by example: a serial forEach promises to invoke its block on
> the caller's thread, and promises not to let it be invoked after returning
> to the caller.  I think you called it 'non-concurrent' on the list, at some
> point.  Longer answer...
>
> Some time ago, while it was being discussed, I was quite in favour of
> having various forms of transparencies that people were advocating, and
> then I've come round to the idea that lambdas shouldn't go that far,
> because a declared goal is to support idioms most suitable to parallel
> execution.
>
> I probably noticed that the use cases advocating the extra transparencies
> were often about flow-control abstraction, and that these are often serial
> in nature, i.e. the control abstraction didn't have to do anything
> concurrently to get its job done, e.g. (non-parallel) forEach, with(lock)
> {... }.  (I'm assuming that this observation is almost always true, but I
> am interested in seeing examples which contradict it, too.)
>
> Some advocated the user of these abstractions being able to turn on
> transparencies - e.g. using @Shared on a local to be mutated by the lambda
> - because they believe the call they are making promises not to invoke the
> supplied lambdas in 'complex' ways.  There's a risk that this will be used
> incorrectly, e.g. when the promise isn't made but is believed to exist, and
> I thought it would be a lower risk if instead the promise was formally
> declared by the method, and that turned on the transparencies.  Rather than
> the user having control over transparencies, and guessing when they would
> be safe, the provider of the method, who is in a position to decide whether
> to make the promise, chooses whether or not to declare it.
>
> So, a very weak serial guarantee that could be made by the receiver of a
> lambda (or other functional object) would be: "I will not invoke the
> functional object on any thread but the one you're invoking me with now."
>  This would be enough to turn on mutable local capture, in some
> Javascript-like asynchronous environment, as was discussed here:
>
> <http://mail.openjdk.java.net/**pipermail/lambda-dev/2011-**
> July/003751.html<http://mail.openjdk.java.net/pipermail/lambda-dev/2011-July/003751.html>
> >
>
> A stronger guarantee would be as above plus: "I will not invoke the
> functional object after returning control to you."  This appears good
> enough to do control abstraction, where the method making the guarantee is
> really just offering to organize the user's code according to some usually
> serial idiom.  You'd additionally permit non-local returns, throws, breaks
> and continues.
>
> Further guarantees would be about the number of times something was to be
> invoked (at least once, exactly once, etc), which I believe you'd need to
> combine control abstraction with definitely-(un)assigned concepts,
> 'continue' and 'break'.
>
> How to express these guarantees...  I thought you could annotate the
> functional-interface parameters, e.g. @Callback for the weakest, @Serial
> for the next.  I don't know if that's good enough, because I'd expect to be
> able to use @Callback as if it were some sort of C-like type qualifier (and
> you'd then consider whether a @Callback Runnable was assignable to a plain
> Runnable), but that might be stretching the meaning, purpose and principles
> of annotations. Perhaps less of a problem for @Serial and stronger - I
> imagine these enabling a certain syntax, which emphasises that only a
> serial idiom is going to be applied, and turns on the corresponding
> transparencies.
>
> Are these guarantees compiler-verifiable?  Feels like not, generally.  It
> might not matter so much.  We already don't expect the compiler to detect a
> Comparator that just returns random values, or a Set that doesn't have set
> semantics.
>
> So, back to the point about Stream.  You only informally get serial
> guarantees with the one returned by Streamable.stream(). (Otherwise, what
> is parallel() for?)  If you declare a method to take a Stream (rather than
> a Streamable[1], for whatever reason), you can't assume that it has come
> from stream(), not parallel().  If you did know it was serial, you might
> feel free to implement your own ad-hoc transparencies that depend on this
> serial characteristic.  Since you don't know, you may as well assume it's
> the parallel one and not take the risk with ad-hoc transparencies - in
> which case, what's the point in having stream()?  At least, without having
> to add any further language changes, if stream() returned a SerialStream,
> you'd have as much a guarantee as you would that a supplied Set is not
> merely a Collection, and you could at least formally express that you
> require a SerialStream if a plain (potentially parallel) one won't do.
>
> Ignoring my ideas of serial guarantees for a moment, and narrow down the
> problem to just exception transparency, suppose you ressurect <throws E>.
>  You could declare a serial forEach as:
>
>   <throws E> void forEach(Block<? super T, E> block) throws E;
>
> For a parallel version, you have to specify how multiple exceptions are
> handled by forEach, or require the block to handle them itself (and drop
> <throws E>).  If forEach handles them, does it throw away all but the
> first?  Merge them in some wrapping exception (which probably changes the
> signature)?  It seems, the most sensible options change the signature, and
> one signature belongs on Stream, and the other on SerialStream.
>
> [1] Hmm, seems I'm out of date already - no more Streamable.
>
> Cheers!
>


More information about the lambda-dev mailing list