Serialization opt-in syntax (again)

Kevin Bourrillion kevinb at google.com
Mon Oct 1 08:57:04 PDT 2012


(Meta-point: our discussion keeps bouncing back and forth between email and
the issue log; what's the right way we're supposed to be doing this?)

I'm sorry to say it, but I for one would still be shocked if that's really
the best we can do.

First, I'm not nearly as convinced as others here that there's a big
advantage in a general solution that applies to any desired interface
types. Serialization really is special. If it weren't for serialization we
wouldn't even be worrying about this multi-interface problem at all. It
just wouldn't rate on our top 20 list of concerns. But serialization is
what it is, so we need to jam an extra interface type in -- but that's
really not *all* that is happening. There is Special Magic you get by
asking your lambda to be serializable (in whatever manner we provide),
that's even beyond whatever Special Magic happens for declaring a regular
class to implement Serializable. Special magic requiring special notation
is not a terrible idea.

I also don't see much benefit to our solution here generalizing to
anonymous classes. If it does, fine, but helping anonymous classes a little
just doesn't *matter* that much, so let's not treat that as more of an
advantage than it is.

In other words, I just think it's completely fair to address the
serialization-for-lambdas issue specifically, head on, and not care *too*
much about how it generalizes.

So back to the proposed alternatives.

I've expressed support for a minimally invasive syntax like (x) ~> x.y().
 While that might not be perfect, compare the need for zero (maybe one)
extra characters with the need, in the example given here, to add a
whopping *thirty-five* extra characters.  That is no small difference in
verbosity.  Also, as we've already found with Map<Key, Value<Whatever>> map
= new HashMap<Key, Value<Whatever>>(), the need to redundantly repeat
generic type information twice on the same line where the two have no valid
reason to ever be different ... is a real drag.

The criticisms of a special syntax like ~> that I have seen here:

* "seems too clever" -- I'll admit, having the serialization concern
intrude all the way into the language syntax itself is not awesome

* can be visually overlooked, like 4321 vs 432l.  Yes, but it seems like
there's a limit on just how much pain and suffering that confusing can
really cause. Meant something to be serializable that wasn't? Okay, change
it. Problem solved?

* doesn't generalize to anonymous classes, other interface types -- I
covered why I don't put much weight on this up above

* doesn't do anything for method references -- hmm, that is a bit of a
problem, right?

I also don't think I would completely rule out the idea of the special
keyword or magic "operator" (Predicate<String> p = serializable (x) ->
x.isEmpty(), Predicate<String> p = serializable String#isEmpty).

I don't like that much, but that's what ALL the proposals have in common...
not a single one of them is likable imho. And AFAIK, having all lambdas be
serializable and having no lambdas be serializable are still not considered
viable. So we're really in a tough spot.



On Mon, Oct 1, 2012 at 7:47 AM, Brian Goetz <brian.goetz at oracle.com> wrote:

> We need to wrap this up.  Does anyone have a *significant objection* to
> the syntax (cast to intersection type) suggested below?
>
> Here is another option that we explored a while ago and initially turned
> our nose up at, but we are still willing to explore: using intersection
> types in a cast to target type, inspired by multiple type bounds in
> generics:
>
>     Predicate<String> ps = (Predicate<String> & Serializable) x->
> x.isEmpty();
>
> The major downsides are:
>  - Verbose (have to repeat the target type)
>  - Another ad-hoc context where we allow intersection types, though offers
> some runway in the event we want to extend the use of intersection types in
> the future.
>
> It extends to inner class expressions in an OK manner:
>
>   new Foo(blah) & Serializable { ... }
>
> Again, the cast syntax can apply to any intersection of types that meets
> the SAM-ness requirement, and the inner class syntax can be any arbitrary
> set of interfaces.
>
>
> On Sep 28, 2012, at 11:48 PM, Brian Goetz wrote:
>
> Bob is definitely right here that the semantics of this are closer to what
> we want than any of the others.  While the proximate problem is "how do I
> make a serializable lambda", the way you make a class serializable in Java
> is (in part) to extend Serializable.  So something that addresses the "how
> to I extend Serializable" question is much more in the spirit of how
> serialization works (for better or worse) than a special magic
> serialization syntax.
>
>
> On Sep 28, 2012, at 4:14 PM, Bob Lee wrote:
>
> I like the semantics a lot! Maybe moving the "implements" decl to the
> right of the -> would address Kevin's concerns? Then it would read "lambda
> (->) implements ..."
>
> Bob
>
> On Fri, Sep 28, 2012 at 1:47 PM, Brian Goetz <brian.goetz at oracle.com>wrote:
>
>> I put all the candidate syntaxes so far in the JIRA issue for this, but a
>> new one came to light this week that we kind of like.
>>
>> The problem is: let's say you have a SAM that is not serializable, but
>> you want the instance to be, such as in:
>>
>>   Runnable r = () -> { };
>>
>> The problem is that we really want to specify multiple interfaces for the
>> lambda, and as long as their intersection has only one abstract method,
>> that should be OK.
>>
>> So, how about using the space between the closing paren and the arrow:
>>
>>   Runnable r = () implements Serializable -> { ... }
>>
>> As a bonus, if we wanted to be explicit about all the implemented
>> interfaces, this easily extends to:
>>
>>   Object p = (String s) implements Predicate<String>, Serializable -> {
>> ... }
>>
>>
>> This also extends nicely to inner class creation expressions.  Right now
>> there is a limit of one named supertype.  But this could be extended:
>>
>>   Predicate<String> p = new Predicate<String>() implements Serializable {
>> ... }
>>
>> In this case, there is no single-method restriction; you could implement
>> Iterator and Runnable if you wanted:
>>
>>   new Iterator<T>() implements Runnable { ... }
>>
>> Note that none of this is serialization-specific; it is simply a way of
>> being explicit about multiple supertypes in contexts there this was not
>> previously allowed.
>>
>>
>
>
>
>


-- 
Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com


More information about the lambda-spec-observers mailing list