Menu

#14 null dispatching (+ a note on relaxed MJ)

open
nobody
5
2012-11-27
2003-02-21
Keunwoo Lee
No

This is something I talked about with Todd a
while back; I was recently reminded of it while
coding and I thought I might as well document it.

It would be nice if generic functions allowed
dispatching on null values. Right now, MJ
always invokes the default case. With this
behavior, we effectively regard null as an
instance of the gf's static type in that argument
position. This is sensible, but leads to some
surprising results for Java programmers.
Consider the following:

import java.util.*;
public void Collection.f() {
if (this == null)
System.err.println("this == null.");
else
System.err.println("ok");
}

In ordinary Java methods, this can never be
equal to null, so Java programmers will not
expect or anticipate this possibility. It may be
that Java programmers will just get used to it as
they transition to MJ, but alternative design
decisions are:

+ Always throw NullPointerException when using
null in a dispatched position. I don't really like
this, even though it's the solution that's the most
consistent with Java's existing semantics.

+ Consider null to be a unique value of every
type, and allow people to write a single case
f(T@@null) for every generic function f(T). In the
Collection.f() example above, you might allow a
case

public void Collection@@null.f() { ... }

(The reason you need Collection@@null.f()
instead of just null.f() is that otherwise you don't
know which generic function named f() to add
the method to.)

This is my favored solution. Sometimes when I
write external functions or multimethods, I find
myself wanting to factor out a separate case for
null values, instead of using an if/then in the
body of the base method.

Furthermore, in the presence of relaxed MJ, I
think that the issue of nulls becomes more
pressing. Namely, consider the following:

class T {}
abstract void T.f();
final class U extends T { void f() {} }
final class V extends T { void f() {} }

Without nulls, we could conclude that f() is fully
implemented until some other class comes
along. But if we naively apply the current MJ
semantics for nulls in dispatched positions into
relaxed MJ, we get an ambiguous method error
when we evaluate ((T)null).f().

This may not sound too bad---"hell, it's a null in
the receiver position! just throw
NullPointerException!"---but if you consider

class W {
abstract void g(T t);
void g(T@U u) {}
void g(T@V v) {}
}

then it doesn't look so attractive to throw
NullPointerException or AmbiguousMethodError
just because the argument to g is null.

Discussion


Log in to post a comment.

Auth0 Logo