Stream proposal and null results

Brian Goetz brian.goetz at oracle.com
Thu May 17 15:27:01 PDT 2012


Your Scala-bias is showing when you say "push it into the type system", 
like that is a good thing :)

Some perspective: some have suggested that this problem isn't even big 
enough for a new library class like Optional -- that using null as a 
sentinel is good enough.  So if this problem is not big enough for even 
one new library abstraction, it is certainly not big enough for a new 
language feature!

What you are asking for isn't exactly static extension methods (since 
you wouldn't want to inject getOrElse unconditionally into Object, but 
only when viewing an Object as an Optional) and isn't exactly interface 
injection (because you'd get fouled up by the fact that your receiver is 
null), but more a swizzling of the two.



On 5/17/2012 6:10 PM, Scott Carey wrote:
>
> On 5/11/12 10:55 AM, "Brian Goetz"<brian.goetz at oracle.com>  wrote:
>
>> Yes, you caught us :)
>>
>> Currently getFirst() is the only method that cheats in this way (using
>> null as a sentinel), and this is generally a step in the wrong
>> direction.  A much more promising direction here would be to do
>> something like Option (Guava) or Optional (Scala) where getFirst returns
>> an Option, rendering it impossible for the user to "forget" to do the
>> null check.
>
> There are discussions in Scala-land about the performance problems with
> Option[A].   What is frustrating is that the object wrapper is not
> necessary for correctness as long as Some(null) is not allowed -- it is
> simply a mismatch between the type system and runtime representation.
> Some have tried to have the compiler never materialize the wrapper object.
>   Others have suggested that union types in the future would naturally
> allow the compiler to do all the work and never materialize such a thing
> at runtime.
>
> I suggest that Java leap-frog this issue and not build an object that is a
> wrapper for an optional value, and somehow let the type system hoist a
> static method into what looks like a method call on the object
> syntactically but is null-safe.
>
> Imagine if the following was possible with defender methods:
>
> ---------------
> interface Optional<T>  {
>
> final T getOrElse(T default) {
>    Objects.getOrElse(this, default);
> }
> ---------------
>
> class Objects { ...
>
> public static<T>  getOrElse(T object, T default) {
>    if(null == object) {
>      return default;
>    } else {
>      return object;
>    }
> ----------------
>
> And similar for the
>    T getOrElse(Callable<T>)
> and
>    void getOrElse(Runnable)
> variants.
>
> Then, no materialization of an option type is needed -- if
>
> java.lang.Integer
> implements Optional<Integer>
>
> Then it pushes the null-safe static method from Objects into syntactic
> sugar for Integer as a null-safe method call on an Integer object:
>
> Integer nothing = null;
> nothing.getOrElse(3);  //<-- no NPE!  it is just syntactic sugar for
> Objects.getOrElse(nothing, 3);
>
>
> In short, I think there are creative ways to push this into the type
> system and not require object boxing.  There would be other uses for this
> besides Option.  Many other uses.   I think this works as long as the
> defender method is final and naming collisions have a good solution --
> this must be invokestatic and never resolve to invokevirtual or
> invokeinterface since the object may not exist.  Maybe there is something
> creative with invokedynamic that is possible.
>
>
> Other methods that I would think go on Optional:
>
> map and flatMap.
>
>
>
>> And, of course, having the return type explicitly represent
>> maybe-null is more self-documenting than the current prototyped
>> approach.  Moving to some sort of Option-like approach here is on our
>> to-do list, but currently we're working through some bigger issues
>> first, such as how the streams framework attaches to collections.
>>
>> A secondary benefit of this approach is that it eliminates a "garbage"
>> variable from the client code.  Such "garbage" variables can often
>> obfuscate the flow of what's going on.
>>
>> So, total agreement; we just haven't gotten around to this yet.
>>
>>
>>
>> On 5/11/2012 1:06 PM, Scott Carey wrote:
>>> In the stream API draft proposal, I noticed that null was used to
>>> signify
>>> a failed computation or filter:
>>>
>>> Method matching =
>>>
>>> Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
>>>           .filter(m ->   Objects.equals(m.getName(),
>>> enclosingInfo.getName())
>>>           .filter(m ->    Arrays.equals(m.getParameterTypes(),
>>> parameterClasses))
>>>           .filter(m ->   Objects.equals(m.getReturnType(), returnType))
>>>           .getFirst();
>>> if (matching == null)
>>>       throw new InternalError("Enclosing method not found");
>>> return matching;
>>>
>>>
>>> It would be very useful to avoid requiring clients to check the result
>>> for
>>> null. This error prone null check pattern can be avoided and pushed into
>>> the framework with something like:
>>>
>>>
>>> return
>>> Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
>>>           .filter(m ->   Objects.equals(m.getName(),
>>> enclosingInfo.getName())
>>>           .filter(m ->    Arrays.equals(m.getParameterTypes(),
>>> parameterClasses))
>>>           .filter(m ->   Objects.equals(m.getReturnType(), returnType))
>>>           .getFirstOrElse(() ->   throw new InternalError("Enclosing
>>> method
>>> not found"));
>>>
>>>
>>> A couple other signatures for the last line are very useful as well:
>>>
>>> .getFirstOrElse(() ->   someDefaultComputation()));
>>>
>>>
>>> .getFirstOrElse(someDefaultValue));
>>>
>>>
>>>
>>>
>>> If the only API contract for a computation with no result is to return
>>> null, then the pattern
>>>
>>> T tempVar = values.someComputations().getResult();
>>> if (null == tempVar) {
>>>     // do something about no result here
>>> }
>>> return tempVar;
>>>
>>> will be extremely common boilerplate that can be avoided .
>>>
>>> I think there are a three signatures that together remove the
>>> boilerplate.
>>>    Below I have changed the name from getFirstOrElse to getFirst:
>>>
>>> void getFirst(Runnable elseAction)
>>>
>>> T getFirst(Callable<T>   elseResult)
>>> T getFirst(T elseVal)
>>>
>>> T getFirst() is then simply a shortcut for
>>>
>>> T getFirst((T)null);
>>>
>>>
>>>
>>>
>>> There is likely something more elegant than multiplying the number of
>>> method signatures for every method that has an optional result.
>>>
>>>
>>>
>
>


More information about the lambda-dev mailing list