On 5 June 2012 11:05, Reinier Zwitserloot <[email protected]> wrote:

> On Monday, June 4, 2012 5:09:43 PM UTC+2, KWright wrote:
>>
>>
>> This now has alarm bells going off in my head.  These (as described)
>> would only make sense if specified at both declaration AND use site, that's
>> an awful lot of boilerplate and ceremony to be added!  We already tried the
>> same thing with checked exceptions, and you know how well that succeeded...
>>
>
> Yes, you need to specify both at declaration and use. Like _ANY_ type. Why
> does that have alarm bells going off in your head?
>
> String x = "Hello"; //site 1
> System.out.println(x);
>
> public void println(String in) { ... } //site 2
>
> That's perfectly normal, no need for alarm bells.
>
>
Not quite.  In addition to the regular type system, we also have the shadow
type systems of annotations and checked exceptions.  This would be a third
shadow system and it *would* add complexity.

I suspect that Nullity in this form could be internally implemented through
annotations to minimise the effort required, yet it would have to follow
different rules in the presence of subtyping (more later).  In practice, it
would feel a lot more like generics - optional, erased, yet still part of
the type system.

Nullity is close to variance in this regard.  With use-site (Java) or
declaration-site(C#, Scala) approaches both being possible.  If I could
statically type Map<K!, V!> when *defining* the class, then using a
guava-esque factory we might have:

public static final Map<String, Integer> example = Map.of("a", 1, "c", 2)


declaration-site nullity.  No extra ceremony or boilerplate in the client
code :)



>
>> Then there's the issue of subtyping and the LSP to consider.
>>
>
> No, the 4-nulls thing actually solves this. Technically, you could treat
> [dunno-null] as "? extends String! super String?" if you really wanted to,
> but you can simplify matters considerably by taking nullity out of the type
> inheritance system. String is a type, and where inheritance is concerned,
> String is just String, and the nullity of it does not enter into the
> equation whatsoever; String, String!, String?, String[raw], it's all the
> same to the type system. As in, == equals - the exact same. If there's a
> mismatch between a type's nullity state and the operation you do on it,
> then the compiler will emit an error (you're treating a could-be-null as if
> it's never-null, or you're passing a could-be-null to something that wants
> a never-null) or a warning (you're doing null-checks on a never-null). This
> errors-and-warnings system is like a compiler plugin, it has no actual
> effect on the meaning of any of the code nor of how any of the code is
> compiled, the ONLY thing that it could possibly cause is errors and
> warnings. It's analogous to @Override in that sense. Just compiler-checked
> documentation is all.
>
> If you want to be technical about it, nullity is its own tiny little
> non-expandable strictly and statically defined hardcoded type system. The
> upshot is that it's all very easy to reason about and there's no risk of
> conflicting with existing specs. For example, you cannot write both:
>
> public static void test1(String? foo) {}
>
> public static void test1(String! foo) {}
>
> in the same file, but that would have been possible if these were actually
> different types.
>

Here's a thought experiment, I have:

public class Foo { ... }
public class Bar extends Foo { ... }
public void doSomething(List<? super Bar!> xs)


What would be a valid argument to doSomething?

List<Bar!>
List<Bar>
List<Foo!>
List<Foo>


This is definitely something outside the current spec, so adding it would
be a mammoth task - possibly the same order of magnitude as both generics
and annotations.



>
>
>>   It's easy enough to say that String is a subtype of both String! and
>> String?, but it massively changes the Java spec (which only allows
>> subtyping through inheritance).  It looks like we'd be in a similar
>> position to having Array[Object] being a supertype of Array[T], and the
>> problems that caused.  Then you still have the midas problem if you need to
>> pass a String where a String! is demanded.  And how does it work inside
>> collections and generics (especially wildcarded ones)? and through
>> reflection?
>>
>>
> See above - no changes needed whatsoever. There is also no midas problem
> here; just because I use this equivalent of Option somewhere does NOT mean
> my code is going to end up with every type in every parameter replaced with
> Option<T> everywhere! Generalized APIs such as List will most likely roll
> with dunno-Ts everywhere but this is completely transparent to ALL
> nullities: You can pass never-nulls, could-be-nulls, and dunno-nulls to
> such an API and it would all work. Maybe you don't understand the midas
> problem? With Option, then I start having List<Option<X>> everywhere,
> severely complicating my API documentation and requiring me to also start
> using option. The entire _point_ of the 4-nullity concept is that null
> safety is transparent yet compile-time checked. Contrast to Option, which
> is compile-time checked but not transparent, and java's system, which is
> transparent but not compile-time checked.
>
>
>
public String! turtleA() { return turtleB(); }
public String turtleB() { return turtleC(); }
public String turtleC() { return ...; }
... continue until you run out of turtles ...


Every method below the top one now has to be changed to return a String!
(unless you provide some form of nullity cast).  Is this not the essence of
the midas problem?



>
>
>>
>> I have no solution for this dilemma, other than introducing an IDE which
>>> exceeds the ascii character set for symbols, and which introduces certain
>>> keyboard shortcuts to change nullity. But that's an entirely different can
>>> of worms.
>>>
>>>
>> Not just the IDE.  You have javadoc, static analysis tools, code
>> coverage, etc. etc.
>>
>
> Nope, those can just use the long-form ASCII symbol that is in the actual
> source file. It's fine in all such tools, but where it gets real tedious is
> in day-to-day edit work, but if your IDE can let you enter the appropriate
> nullity state very easily, and render it in an unobtrusive manner, you've
> gotten your 90% in and it's fine then.
>
>
>>
>> It's a bold solution, to be sure.  But the work and complexity required
>> to retrofit it look more complicated than Option[T] at this stage.  That's
>> before you even consider composability.
>>
>
>
> Hah, just... no. It is not possible to retrofit java to Option<T> because
> that is not transparent; APIs are set in stone, you can't change them.
> 4-nullities is transparent which means that's actually an option, al though
> it is very complex.
>
> Sure.  Retrofitting is *always* harder than getting a clean design in the
first place.  This is why C# forked the collections when they added
generics.  I also consider Scala's collections to be a similar fork - not
just adding option awareness, but also immutability at the root of the
hierarchy and all sorts of monadic goodness.  Other such examples are
Google Guava and the totallylazy library.  This can happen at the library
level without requiring sweeping changes to the type system in the language
spec.

-- 
You received this message because you are subscribed to the Google Groups "Java 
Posse" group.
To post to this group, send email to [email protected].
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