Okay, most of those aren't low hanging fruit. I've spent the past week
working out specifically how to translate from source to source and
its just too difficult for many of the changes I had in mind. CICE,
rewiring == to use .equals, and value classes are really the only big
ones that 'work'.

I'll explain the insane complexity of non-nullness just as a single
example to show how extraordinarily difficult being compatible with
java (even on the class file level) is:

non-nullness seems like a simple thing to add. For arguments sake,
lets say that a standard type is nullable, and a suffix ! indicates
never null. (I know the prevailing attitude is that non-null should be
the default, and I sort of agree with this, but its easier to explain
the difficulty this way, so bear with me. Its easy to see that if you
can make '!' work, then going the extra step and making that the
default, and '?' as a marker to indicate nullity, is trivial, so it
doesn't really matter which one of the two gets proved to be nearly
impossible to implement).

So, here's the ! marker in action:

  List!<String!> list = new ArrayList<String!>();
  list.add(null); //compiler error; list contains 'String!', so null
is not allowed.
  list = null; //compiler error; list is of type List!, so may not be
null.
  String! foo = list.get(0); //okay
  String bar = list.get(0); //okay; returns a String! but that is
assign-compatible with String.
  String x ="foo";
  list.add(x); //not okay. x might be null.
  list.add((!)x); //okay. (!) is shorthand for 'throw NPE if
expression is null'.

Because the ! suffixes are everywhere, including in method return
types, you won't be using (!) all that much. Also, compiler analysis
will be smart enough to recognize 'if ( foo == null ) { /* code */ }
else { /* code where 'foo' is treated as non-null */ }. This is great
for e.g.:

  Map!<Integer!, String!> map = {1 -> "one", 2 -> "two"}; //pseudocode
  List!<String!> list = new ArrayList<String!>();
  String! foo = map.get(0); //NOT OKAY - .get() returns null to
indicate not found.
  String x = map.get(0); //this is okay.
  if ( x == null ) { sysout("not found"); } else { list.add(x); } //
okay - compiler analysis.

Bit more complicated, but we can make this work.

Now we get into the nasty bits: generics.

List<String!> list = new ArrayList<String!>(); //okay
List<String> list2 = list; //is this okay?
list2.add(null);
String! x = list.get(0);
//Ah. So, that's NOT OKAY. But then...
List<String> a = new ArrayList<String!>(); //ALSO NOT OKAY
List<String!> b = new ArrayList<String>(); //NOT OKAY EITHER

In the above code snippet, 'x' would contain null, eventhough its of
type 'String!'. Between obvious speed issues and a massive set of
existing libraries that can't just be recompiled, its not possible to
runtime-check everything, so the type system needs to ensure
correctness. This is just like generics, which also isn't runtime
checked. And so we have the same issues. Just like List<Number> c =
new ArrayList<Integer>(); is NOT LEGAL in current java, List<String> d
= new ArrayList<String!>() can't legal either, because you can use it
to break the forced non-nullity.

Assigning a List<String> to a variable of List<String!> is also not
okay, for perhaps more obvious reasons.

Just like generics, if we somehow had a guarantee that we're only
going to read from the list, you COULD assign a List<String!> to a
variable of type List<String>; the fact that we have to do null-checks
on items retrieved from the list eventhough they aren't ever null is
not a problem. Similarly, if we somehow knew that we're only going to
write into the list, then assigning a List<String> to a List<String!>
variable is okay; we'd be forced to only write non-null into the list,
which is of course always okay, regardless of whether the list allows
nulls or not. This is like generics '? extends String' and '? super
String'. You can read Strings from the first (but not write them), and
you can write strings (but not read them) into the second. Co/
contravariance, in other words. And so, non-nullness has the same
complexity as generics. We need a way to say that we don't know the
forced non-nullity status of a generics bound. Let's make up a syntax
for this:

List<String?> list = new ArrayList<String!>();

The ? doesn't indicate 'might be null', but instead indicates 'might
or might not be forced non-null'. As a result, when adding to this
list, you must pass in String!, and when reading, you get String (so,
adding stuff must not be null, but when reading you must be prepared
for nulls). Just like with ? extends String, when reading you get a
String, but you can't write to it. Imagine we used non-null as a
default and '?' as a marker that it might be null, what symbol do you
use to indicate this now? Interrobang? Something like List<?!> list?
We'll get back to that; lets continue with 'null is default, !
indicates non-null, ? indicates unknown state' for a moment.

We need the ? for return types as well. ArrayList can of course hold
null if it wants to, but its get() method returns null only if its
generics parameter is a nullable type. However, for the V part of a
Map, this isn't true; its get method can always return null. For
another class, lets say a hypothetical NullMarkedLinkedList, you MUST
always pass in non-null types, so it would make sense to throw a
compiler error right at the construction phase and disallow: new
NullMarkedLinkedList<String>(); - only allow passing <String!>. We
need ways to mark this. Let's go with:

public class NullMarkedLinkedList<T!> implements List<T> {}

The T! there indicates that the generics bound MUST be non-null.

and for the map class which needs to explain that its get method can
always return null:

public V? get(K key) {}

This means: Regardless of the allows-null status of 'V', this method
can always return null.

Okay, so far so good, sort of. It's already miles more complicated
than any of you originally thought. I was certainly bummed when I got
this far. But we're by no means done, unfortunately.

Lets now add full generics in the mix.

List<? extends Number> list = new ArrayList<Double!>();

That's seems illegal, for the same reason List<String> = new
ArrayList<String!>() is not legal. Except that this one IS legal,
because, due to the 'extends', we've already limited ourselves to only
reading from 'list' (try it; you can't add things to that list unless
you do an unsafe  generics cast which produces a generics warning).
However, this is even more complicated than generics: List<T> =
someList<T!> is illegal, but List<? extends T> = someList<T!> isn't.
Whoa.

But we need a way to indicate that 'list' is still definitely non-
null, so that when we read from it, we get 'Number!' out, instead of
'Number'. Let's use the interrobang for this one:

List<?! extends Number> list = new ArrayList<Double!>();

Unfortunately the ?! syntax looks a bit like cartoon swearing but it
seems like the most sensical thing. Putting the ! on Number seems
nicer, but that doesn't really make any sense. The Number is just a
bound, its not a complete type. The actual thing which isn't going to
be null is the type itself, not the bound, which in this case is
represented by the ?.

Ugh. We now have:

T - no non-null bound
T! - definitely not null
T? - may or may not be forced non-null
?! - nameless generics variable with forced non-null

and we're still not done - there's 'super' as well:

List<? super Integer> list = new ArrayList<Number!>(); //not okay
List<?! super Integer> list = new ArrayList<Number!>(); //okay
List<?! super Integer> list = new ArrayList<Number>(); //NOT OKAY!

Now the reverse happens; the ?! notation will accept either forced non-
null or not-forced-non-null because due to the 'super' we've already
limited ourselves to just writing things into the list. ?! forces us
to write non-null Integers, which is obviously also okay to do to a
list that contains nullable Numbers (a non-null Integer is obviously a
Number which may or may not be null). Unfortunately, unlike the '?
extends Number' case, where it's impossible to write to the list, you
*CAN* still read from this list. You'll just get Object back, which is
annoying, because Object also has nullable/non-nullable status. Either
the List<?! super Integer> returns "Object" for its get method,
instead of "Object!", which seems completely screwed up, and makes it
impossible to pass non-nullity status to stuff with generics 'super'
bounds (as even the ! doesn't actually say: Never null). So, we need
yet another syntax, the double question mark:

List<?? super Integer> list = someList();
Object x = list.get(0); //okay
list.add(null); //not okay
list.add(10); //okay

I think we're done, but at this point I'm cleaning my exploded brains
off the walls, so who knows if this is where the insanity ends. (I
checked what IDEA does, but IDEA just doesn't allow the annotations
they use inside generics bounds, which seems like making the entire
null checking stuff pointless. I'd ask IDEA people how useful the very
limited nullability checking is, but I'm afraid of fanboyism, so,
please be elaborate and use examples)


Even if you compiled straight to class files a la scala, you can't fix
this one without making life very very difficult for yourself.



By the way, viktor, you're still not getting it.

For example, you can't add tailcalls to the JVM without *CHANGING THE
JVM*. Not even scala goes this far!

Here's your scoreboard (source-source is source rewriting, source-
class is compiling directly to class files, which wasn't the idea, and
JVM-change means changing the JVM, which is completely off the board).
duplicity test = because of the translation back and forth, only 1
version is allowed. So with type aliases, for example, if you aliased
'StringList' to 'List<String>', then anytime you typed 'List<String>',
the source would forcibly be changed. This would get annoying, I bet.

1) Remove checked exceptions: FAIL: source-class
2) Add type aliases: PASS: source-source, but duplicity concern.
3) add value classes: PASS: source-source
4) add non-null: FAIL: complexity (see above)
5) add methods to interfaces: If you meant s tatic methods, PASS:
source-source (stuff methods into a $Methods inner class). If you
meant traits, FAIL: extensive source-class also of code that
implements it. (note how you can't implement scala traits in java
code, for example)
6) add CICE: PASS. source-source.
7) tailcails. FAIL: JVM rewrite
8) permgenspace goes away: FAIL: JVM rewrite
9) message-based concurrency: N/A: That's library stuff; the only
thing you can do is add closures and a few other primitives so you can
write for it in a sane way, which 'add CICE' mostly covers.


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "The 
Java Posse" group.
To post to this group, send email to javaposse@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/javaposse?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to