Stream proposal and null results
Howard Lovatt
howard.lovatt at gmail.com
Thu May 17 18:08:32 PDT 2012
I think it would be an interesting idea to inject getOrElse(T) and
getOrElse(()->T) into Object. They would have wide
usage beyond the lambdafied collection library.
With regard to the collection library; I have found a parallel collection
that does not accept null to be useful, since your maps etc. don't need to
null check and the collection can use null internally to mean skip over
item. If the collection didn't hold null, then returning null as
a sentinel is less problematic.
-- Howard.
On 18 May 2012 10:35, Scott Carey <scottcarey at apache.org> wrote:
>
>
> On 5/17/12 3:27 PM, "Brian Goetz" <brian.goetz at oracle.com> wrote:
>
> >Your Scala-bias is showing when you say "push it into the type system",
> >like that is a good thing :)
>
> Scala folk say I'm Java-biased.
>
> Lets re-phrase: "I want the compiler to help users prevent mistakes with
> results that may be null". Or "there should be a better Java convention
> than wrapping every result with a temporary variable and null check".
> Adding extra operators like the once proposed '.?' is not a general
> solution ( what about getOrElse(Callable) or transform/map ?).
>
> I rarely code with Scala, I suppose I am 'biased' because spent time on
> mailing lists on several JVM languages, and have experimented with some
> prototypes in several. Java is still my day-job. Currently, the Lambda
> project is very interesting to me because it could solve several design
> issues in Apache Avro representing an API that implements functions to be
> composed together dynamically for dynamically flexible serialization.
>
>
> I'm not suggesting Java get all scala-complicated. Having the compiler
> help prevent user errors with null is not that far away from having the
> compiler know that something is an Integer and not a Long. Its not a
> suggestion for something like higher-kinded types or union types.
>
> >
> >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!
>
> I agree that a library class like Optional or Option is not desirable. My
> view is that an Option/Optional object is a hack with bad performance
> characteristics that causes almost as many problems as it solves (does
> that still sound Scala biased?). You can use Google Guava or write your
> own if you want it -- that uses Java's current type system features to
> provide users with some null safety, at the expense of memory,
> performance.
> Rather than do that, I am suggesting that such an object (in the language
> library or in Google Guava) is not necessary if the language had better
> ways to deal with using an object that may be null.
>
> Having written much code and libraries that have used null sentinels in
> Java and dealing with the downstream consequences later, I hope for some
> progress here.
>
> I am not suggesting that returning a null sentinel is a bad idea. I am
> suggesting that there is a need for a better way to operate on a
> potentially null result than write a conditional statement and keep a
> temporary variable or resort to boxing it in something like Option. It
> seems natural that Lambda relates to this since it allows one to easily
> build compositions of functions, and some of those functions will return
> possibly-null results.
>
>
>
> >
> >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)
>
> I suppose injecting it into Object is possible (if namespace collision is
> somehow dealt with). My proposal is not the only way to deal with this.
>
> >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.
>
> I don't know enough about the guts of the JVM or the lingo to label it
> appropriately. I also have no idea how challenging it is to do. Perhaps
> there are major issues that make it a non-starter. Perhaps it is a minor
> change from the current defender methods. I simply hope that the idea is
> considered as an alternative to introducing something like Option, that is
> potentially more general purpose and flexible.
>
> It would be very useful (and have good performance characteristics) if API
> designers had the ability to make a static method with the right type
> signature appear to syntactically be used on the return from a method
> call. Perhaps this idea helps someone else come up with a better idea.
>
>
> Maybe having the static methods on Objects is decent for now:
>
> return
> Objects.orElse(Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredM
> ethods())
> .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
> .filter(m -> Arrays.equals(m.getParameterTypes(),parameterClasses))
> .filter(m -> Objects.equals(m.getReturnType(), returnType))
> .getFirst(),
> () -> throw new InternalError("Enclosing method not found"));
>
>
> That is hard to follow because you can't chain static method calls.
> The below is easier to follow:
>
> 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))
> .getFirst()
> .orElse(() -> throw new InternalError("Enclosing method not found")); //
> a static method, somehow made chainable from the result of getFirst()
>
> or even:
>
>
> return enclosingInfo.getEnclosingClass().getDeclaredMethods()
> .asList() // may not be necessary if filter or a toStream() is added to
> Object[] ?
> .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
> .filter(m -> Arrays.equals(m.getParameterTypes(),parameterClasses))
> .filter(m -> Objects.equals(m.getReturnType(), returnType))
> .getFirst()
> .orElse(() -> throw new InternalError("Enclosing method not found"));
>
>
>
>
>
> >
> >
> >
> >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.
> >>>>
> >>>>
> >>>>
> >>
> >>
>
>
>
>
--
-- Howard.
More information about the lambda-dev
mailing list