My favorites | Sign in
Google
                
Search
for
Updated Nov 20 (4 days ago) by christianedwardgruber
Labels: Proposal, Deprecated
NullObjectPatternProposal  
Null reference handling.

NOTE: THIS PAGE IS DEPRECATED We've absorbed the comments and some other thinking, and have an alternative proposal in the works.

author: rdionne, christianedwardgruber

Introduction

In an effort to tackle the newable vs. injectable problem, we came up with the following example that led to including the Null object pattern directly in the language. I believe Scala allows similar practice by creating singleton objects that directly inherit from an abstract class.

Notes

The newable vs. injectable issue is not handled in this proposal. Some noop-specific variations from Java that are under discussion are in play in these examples, such as readonly, writeonly, optional, etc., keywords. Please treat these as pseudocode in effect, since these have not yet been finalized.

References

We are assuming that newable objects are mainly data or value objects (or entity objects) that may have additional convenience calculations attached to them. These convenience calculations may be dependent upon the presence of optional internal delegates. There are many situations where an object reference may be null. If, for example, the object uses an optional delegate, how does a value object perform calculations when an optional delegate objects are not present? Usually developers add conditional code to check for null and perform alternate calculations when optional items are missing. We propose supporting two key language features:

1. An objective-c approach to null references 2. Null Object Pattern within NººP.

By doing so, much null-related conditional logic can be replaced with polymorphism.

Invocation against null-references

When a variable reference is assigned to the null value, we allow calling methods upon the reference, similar to behaviour found in Objective-C. The default behaviour of a method with no return type is a no-op. The default behaviour of a method with a return value is to return null:

Foo foo = null;
foo.doFoo();  // Does not throw NullPointerException, performs a no-op by default
assertTrue(null == foo.getFoo()); // Does not throw NPE, performs a no-op and returns null by default

Chaining of methods results in a chain of this no-op behaviour.

BoogaResult result = foo.doBar().processBlah().boogabooga();

This would not throw an exception, but would, if any of the above returned null (or if foo was null) would result in null being assigned to result.

Null Object Pattern

We allow the developer to override the behavior for a null reference based on its type. For example, given a type Foo:

interface FooInput {
  void increment();
}

interface FooResult { ... }

interface Foo {
  void doFoo(FooInput input);
  FooResult getFoo();
}

null Foo {
  void doFoo(FooInput input) {
    input.increment();
  }

  FooResult getFoo() {
    return new FooResult(5);
  }
}

Perhaps such defaults could be provided at a Module level, through injection bindings:

null Foo(FooResult default) {
  void doFoo(FooInput input) {
    input.increment();
  }
  FooResult getFoo() {
    return default;
  }
}

In the following example, a ShoppingCart object groups LineItems representing products on a purchase order. Each line item points to its product, a Purchaseable, its base price, a Money instance, and an optional Discount, which calculates the final price of the product after the applied discount. Rather than include conditional code within LineItem that worries about what to do when a creator creates a LineItem without specifying a Discount, we provide a smart null implementation of Discount:

interface ShoppingCart {
  void addLineItem(LineItem item);
}

interface Purchasable { ... }

interface Currency { ... }

interface Money {
  Currency getCurrency();
  long getAmount();
}

interface Date { ... }

interface Discount {
  Money apply(Money basePrice, Date date);
}

null Discount {
  public Money apply(Money basePrice, Date date) {
    return basePrice;
  }  
}

createable LineItem {
  readable Purchasable product;
  readable Money basePrice;
  optional readable writeable Discount discount;
  readable virtual Money finalPrice {
    get {
      return discount.apply(basePrice, Date.now());
    }
  }
}

Usage:

ShoppingCart shoppingCart = ...;

Purchasable catamaran = ...;
Money catamaranBasePrice = ...;
Discount catamaranDiscount = ...;
LineItem catamaranPurchase = new LineItem(catamaran, catamaranBasePrice, catamaranDiscount);

assertTrue(catamaranPurchase.finalPrice < catamaranPurchase.basePrice);

Purchasable carousel = ...;
Money carouselBasePrice = ...;
LineItem carouselPurchase = new LineItem(carousel, carouselBasePrice); // No discounts here

assertTrue(carouselPurchase.finalPrice == carouselPurchase.basePrice);

shoppingCart.addLineItem(catamaranPurchase);
shoppingCart.addLineItem(carouselPurchase);

Money grandTotal = shoppingCart.total();

Comment by masklinn, Sep 17, 2009

re: Invocation against null-references

Not a very interesting idea in statically-typed languages, hides bugs and misbehaviors which could easily be exposed by the compiler

Re second case, better (in my opionion) solutions use the type system rather than extending the language in pretty weird ways. In both ways, references are not nullable by default, Foo foo = null is always an error.

ML/Haskell-style Option type

The combination of an abstract Option<T> (ML, Scala) or Maybe<T> (Haskell) type (note: generics notation using Java's) and a pair of concrete types Some<T>|None (ML, Scala) or Just<T>|Nothing.

Some/Just contains a value (of type T), None/Nothing represents a "null".

Very nice to use with pattern matching, not as nice when used as objects (it can work well, see scala, but the hierarchy might be kind-of overkill, depending on how you handle things internally)

C#-style Nullable<T>

Equivalent to Option, but is a concrete type handling the behavior of both Some/Just and None/Nothing in a single concrete type, accessed through unboxing properties and methods (in C#, HasValue, GetValue and GetValueOrDefault(T)), offers pretty much the same safety.

Lifting

An interesting property of nullable types, which allows the language's user to replicate the behavior of the "invocation against null-references" safely (type-wise) and if he so desires only is the concept of lifting: generally, if you want to apply an operation to a value and return null (or equivalent) if there is no value, you'll have to manually test for value, unbox, apply the operation and rebox. Using Scala's option type, this yields something along those lines:

if(myOptional.isDefined) {
    return some(myOptional.get().someOperation());
} else {
    return none();
}

This code is verbose, and mostly uninteresting. Lifting allows the language's user to move an operation into the nullable type and abstract all the boilerplate here to directly apply the operation to his nullable. C# auto-lifts some operation since Nullable can only be applied to a few predefined value types but this doesn't work for the general case.

The possibilities here would either be pretty deep compiler magic where operations valid on T are also valid on Option<T>, but return Option<U> instead of U, or the ability to inject arbitrary code through blocks/lambdas:

return myOptional.get(\(myValue) {
    return myValue.someOperation();
});

where get is basically defined as:

Option<U> get((T -> U) func) {
    if(isDefined) {
        return some(func(get()));
    }
    return none();
}

and the initial suggestion could be written something along the lines of:

Option<Foo> foo = none();
if(foo.isDefined) { foo.get().doFoo(); }
assertTrue(none() == foo.get(\(f) { return f.getFoo(); })
Comment by pennyauctionguy1, Sep 18, 2009
Not a very interesting idea in statically-typed languages, hides bugs and misbehaviors which could easily be exposed by the compiler

Interestingly bad perhaps -- making silenced exceptions into part of the language.

What I would like is for the language to get involved directly, the following

Option<Foo> foo = somePossiblyNullValuedFunction();
foo.myMethod();

should throw an compilation error "Possible Null Reference". You would have to write

Option<Foo> foo = somePossiblyNullValuedFunction();
ifnotnull (foo) {
   foo.myMethod();
}

Note that ifnotnull is a statement, not an expression -- the compiler is made aware of the null-ability of a variable and variables can explicitly shed that null-ability in a branch. Under this proposal,

Foo foo = somePossiblyNullValuedFunction();

would simply be a type-mismatch -- you cannot assign a possibly-null expression to a not-null variable.

Comment by andersk@mit.edu, Sep 18, 2009

A static null-safety solution along these lines was recently implemented for Eiffel. This is worth reading:

http://s.eiffel.com/void_safety_paper

Comment by robertsdionne, Sep 18, 2009

@pennyauctionguy1: I like the concept of the Option<T> class. I imagine it could be defined with an expanded generics concept somewhat like below:

class Option<T>(delegate T possiblyNull) {

  public <ReturnType, Method, ArgumentsType> ReturnType Method(ArgumentsType arguments) {
    if (possiblyNull != null) {
        return possiblyNull.Method(ArgumentsType arguments);
    } else {
      return ReturnValue.null;
    }
  }
}

The syntax above is trying to expand generics to allow aspect-oriented null checking of the delegate. Ideally the above method declaration would add the null check to all methods of the type T.

This would let you do:

interface Foo {
  Bar doBar();
}

Option<Foo> maybeNull = fileSystem.readFooFromDisk();
Bar bar = maybeNull.doBar(); // No worries about NullPointerException if maybeNull == null
Comment by rodion.moiseev, Sep 19, 2009

If you are to change the behaviour of invocations on null-referenced objects, I think you must replace the null keyword with noop or something more descriptive. After all, that is what you mean, right? Leaving it as null will only cause misunderstanding.

Alternatively, scap null completely to discourage its usage. If one really must return null from a function make them explicitly declare the null implementation and return that.

null Foo { ... }

Foo method(){
  return Foo.null;
}
Comment by masklinn, Sep 20, 2009

> should throw an compilation error "Possible Null Reference".

Both the Option and Nullable structures in my post handle it that way, unless you add implicit lifting to the language.

> Note that ifnotnull is a statement, not an expression

Why make it a statement when it could trivially be an expression? That complexifies the language's syntax and there's no gain to it.

Comment by rosti.bsd, Sep 20, 2009

Interesting proposal. However I dislike the proposed technique of overriding the default behavior. I would sugges to use static methods with the same signatures for such an overriding.

Let's see an example. Assume we have the following Java-like class declaration:

public class ClassA {

	public void meth() {
		System.out.println("not null");
	}

	// overrides the default behavior of calling ((ClassA) null).meth()
	public static void meth() {
		System.out.println("is null");
	}

	public static void main(String[] args) {
		ClassA a = null;
		ClassA b = new ClassA();

		a.meth();
		b.meth();
	}
}

Then running that code by Noop would produce the following output:

is null
not null

P.S. Java already warns when a static method is being called as a method of an object and not as of its class.

Comment by reinierz, Sep 21, 2009

Get rid of null. You don't need it. Take the null object pattern as proposed, rename it to 'default object pattern', and make a slight tweak to the specs:

There is no null; instead, a dangling variable declaration (a declaration with no initializer) is assigned the default object of the type of the reference. Like the null object pattern, bad things happen if your null/default object is NOT immutable, so that's a problem both of these proposals have.

In noop, all types have a null object implementation; it's just that by default, they do nothing and return void/0/0.0/false/null, and the chaining effect takes care of the rest. Why have this, though? If a given type does not have suitable default behaviour, trying to work with an uninitialized variable should be an immediate error. This is in fact how the default object pattern works: If you try to create a field named "Object x" (presuming Object has no default value, which it shouldn't), that's not legal and the compiler will simply complain. Many types have a perfectly suitable default type: The empty string, the empty list, the empty set, the empty map, the default file system, the default random number generator, etcetera.

However, with the default object pattern, the reference is really pointing at it. This means 2 different module systems can share references and they won't magically change behaviour because one module's dependency listing contains a different null object implementation compared to the other one. The meaning of such things as trying to cast the null and trying to instanceof it also work out much nicer when the reference is real and not a proxy; with the null pattern, you can cast a perfectly servicable List (the empty list, null object for List), and cast it to a String, and not get any errors. Kind of a puzzler, isn't it? Similarly, if you take a List that's working just fine, you can check if it is 'instanceof' List, and get false back, because you are in fact working with null and the behaviour is from the null object. None of these weird puzzlers show up in the default object pattern, because the reference is real. There would be absolutely zero difference between "private Integer i = Integer.valueOf(0);" and "private Integer i;", they would mean exactly the same thing. (presuming Integer.valueOf(0) is defined as the default Integer, but given that the default 'int' is 0, that seems smart).

For those situations where you really do need a way to declare the lack of a result (e.g. Map's get operation), include a scala/haskell-esque Maybe type. The default object for the Maybe type is of course the None singleton. Nice how that works out; "Maybe<String> foo;" is initialized automatically with None(), which is what you want.

Whenever you are interopping with other JVM languages, the null object pattern works slightly better than the default object pattern, but not by much; the default object pattern handles the potential for incoming nulls like so: The incoming object must be reference-typed, for example by first being stored in a variable, or checking at the type of the parameter where you're trying to use it. If this reference type has a default object, it's fine; the generated code does a null-check, and if null, returns the default object instead.

If the reference type does not have a default type, then you've created a compiler error: You're assigning something which is potentially null to something which cannot handle that. We need a new compiler magic rule: Any value of reference type T is autoboxed to Maybe<T>, with the appropriate behaviour - if the reference points at null, then the autoboxing gives you None(), otherwise it gives you Just<T>(theObject). As Maybe<T> itself DOES have a default type, there's no further need for special grammar rules around interopping with non-noop code: Maybe is a legal recipient of interop-obtained objects, and the autoboxing rule takes care of everything else.

I'm not going to delve too deeply into the concept of bottom type here, but for those who know what that is: In a language that allows you to express just about every concept, there would have to be the bottom type, there's the "instance" of bottom (endless loop, throwing an exception, exiting the VM, etcetera), there's "null", and there's null's type, let's call it Void for now (though java's own Void is NOT a proper version of 'the type of null'). The naming of all these concepts is going to become hopelessly confusing, as well as the fun question of: If the null type is compatible with everything, but nothing is compatible with the bottom type, is the null type a subtype of bottom? (answer: Probably not, but, you see how this is confusing). If you only have 1 dimension out at the edge of the type system (just bottom, and no longer any concept of null), you can reasonably add the notion of the bottom type to noop without making things hopelessly confusing. Having more options for (future?) growth is nice to have, and even having the concept of null crimps future growth.

Thus, dump null. null is usually considered a neccessary evil, but this comment should show you that it is not in fact neccessary, and therefore it should be dumped forthwith.

Comment by rodion.moiseev, Sep 21, 2009

I totally agree with @reinerz. null introduces too much slack into the language, because it not part of any type but just a magical thing that seems to apply to anything yet do nothing. It is difficult to document and compiler-check and thus lets errors and discrepancies slip through.

Default types are ONE with their type (i.e. are typed), meaning better potential for explicit documentation of the default type, and thus less errors from the side of clients who use the API (external as well as internal). When default type is inappropriate, using Maybe<T>, as suggested by @reinerz, will give more suggestion to the client that the return value may be undefined (i.e. null) and he/she should do something about it. Maybe<T> keeps type information and therefore easier to check statically.

I believe, null is an unnecessary freedom that leads to vague APIs, external and internal. When designing or refactoring Java modules, I start with nulls but always find myself replacing all nulls with custom default types or Maybe<T> types in the end. Since these have a class of their own they are well documented and therefore easier to use/maintain/test.

Comment by rosti.bsd, Sep 28, 2009

If there is no null, how the garbage collector can work?

Comment by ma...@mindcraftinc.com, Oct 04, 2009

If there is no null, how can the garbage collector work? Good question and it drew my attention to a related issue.

I initially thought that the answer is easy: When variables are replaced or go out of scope their old contents are candidates for garbage collection:

void myMethod() {
  Metal m = new Metal("iron");
  m.doSomething(...);
  m = new Metal("aluminum"); // GC can collect the first object (iron);
  ...
}
// GC can now collect the second object (aluminum)

But we have to be careful about how to handle non-trivial situations:

Metal m[] = new Metal[1000];
// loop to initialize m[0] to m[999]

What is the value of m0? before the loop runs?

  1. We can allow an array to be uninitialized (C-style). I think that this goes directly against the philosophy of the JVM and has very little merit.
  2. We can mandate that an array type should have a default object. This seems like a serious limitation to arrays.
  3. We can mandate that an array type should have a default constructor. This is not as serious a limitation, but it's a waste to create a default object just to have it immediately replaced. It's particularly undesirable with expensive resources like database connections.
  4. We can mandate that an array type should be nullable. This may be an acceptable solution. It's backward compatible for all non-native types.
  5. My preferred solution is to check that if an array item is use an internal unset flag to check if an array item is set before it's accessed. Therefore this code:
  6. Planet p[] = new Planet[8];
    Planet q = Planet[3];
    would produce:
    Test.noop:Line 15: Variable Planet p[3] might not have been initialized.
Comment by w...@beguelin.info, Oct 05, 2009

@reinerz, rodion.moiseev: If I understand you correctly, you essentially want to replace null with Maybe<T>. So instead of a NullPointerException?, I would get a "Maybe<T> is undefined" exception at runtime. I think this is only a marginal improvement, which does not justify a new language. I also don't like the idea of null-implementations. Example: An object uses a database connection. The implementation gets injected. The injection somehow fails. In this case, I want to see a proper error message, and not get some dummy-default-database connection which returns dummy values. The goal of a new language should be to be able to figure out such problems at compile time. The compiler should detect if a variable is used before it is properly initialized. This probably limits the options of how dependency injection can be implemented to constructor injection.

Comment by masklinn, Oct 06, 2009

@rosti.bsd > If there is no null, how the garbage collector can work?

Same as usual: it collects garbage, garbage is objects without references to them, references to objects disappear when they're out of scope.

@ ma...@mindcraftinc.com > But we have to be careful about how to handle non-trivial situations:

The solution to that one is trivial: remove arrays from the user-visible language. Or remove this initialization method, mandate that array be initialized "filled" (either through new int[] {1, 2, 3, 4} or through collection.toArray()).

It's not like arrays are very interesting as anything other than an optimization technique (and an underlying implementation detail) when you have array-backed lists in the user-fronting API.

Comment by rosti.bsd, Oct 09, 2009

Here is a simple Java code example of a recursion method that throws the OutOfMemoryError? when the b = null line is disabled.

public class TestClass {

	public static void meth(int n) {
		if (n > 0) {
			byte[] b = new byte[1024 * 1024];

			// some other code would be here

			b = null; // otherwise memory leaks!!
			meth(n - 1);
		}
	}

	public static void main(String[] args) {
		meth(100);
		System.out.println("Finished.");
	}
}

To run it with or without the null assignment use the following command:

java -Xmx1M TestClass?

Of course, instead of b = null you can assing to "b" a reference to some zero length byte array. But what if "b" belongs to some more complicated class? What if its object's size cannot be defined as or be related to any of the constructor's parameters?

Comment by ma...@mindcraftinc.com, Oct 12, 2009

rosti.bsd: Good point.

There is a reason programmers dislike null.

  • null break the object "contract" so programmers have to check for null unnecessarily often.
  • It leads to abuse, objects are set to null as a normal status indicator, and the resulting code makes us all hate nulls.

Nevertheless, it appears that there may be legitimate uses for null. I am not sure if we should throw out the whole concept because of the potential for abuse.

After some thought, I am considering an alternative. I am not sure if this is a good idea, I am looking for intelligent feedback and I think that this is the forum to get it:

  • An object starts in an undefined state, until it is set. (undefined is the new null.)
  • When tested, Undefined objects evaluate to a Boolean false
  • The following is invalid:
  •    a = null;
       a = undefined; // Undefined is not a keyword
  • The following is valid:
  •   if (isDefined(a)) ...;
      if (a) ...;            // Not the same as above, because isDefined(false) is true
      GC.release(a);         // I.e: Runtime.getRuntime().gc()
      // The preceding line is a suggestion for the Garbage Collector to clean up a 
      // and all of its members if they are not referenced by other objects
  • This is how it would behave:
  •   Civilization c, d, e;
    
      c = new Civilization("Minoan");
      String s = c.getName();          // s == "Minoan";
      s = e.getName();                 // s is now undefined
      d = new Civilization("Mayan");
      s = d.getName();                 // s == "Mayan";
      GC.release(d);                   // d is now undefined
      d = c;                           // d refers to Minoan Civilization
      GC.release(c);                   // c is undefined, but d still refers to Minoan Civilization
      d = e;                           // d is undefined

The advantage is that we actively discourage using the undefined state as a valid state without completely eliminating the concept of null.

Comment by ma...@mindcraftinc.com, Oct 12, 2009

One more example:

  Civilization c, d;
  String s;

  c = new Civilization("Inca");
  s = c.getName().replaceAll("nca", "NCA").lenth(); // 4
  s = d.getName().replaceAll("nca", "NCA").lenth(); // Undefined, no exception is thrown
Comment by rosti.bsd, Oct 15, 2009

In my opinion programmers dislike null because it almost everytime generates the NullPointerException?. I mean, in most cases it has only one constant behavior. To override this behavior programmers need to write an additional code in many places or use special techniques to avoid null dereferencing.

The NullObjectPatternProposal tries to make such tasks easier. According to this proposal null dereferencing would not, by default, throw any exception and a programmer would be free to override this default behavior for individual classes.

In my opinion static methods might be used for doing that overriding in the Noop programming language. I think Noop can even keep the NullPointerException?.

Furthermore, Noop can add a new keywork - strictnull that, when used in a class definition, assigns Java's behavior to dereferencing null of that class. For example:

class A {
    public void meth();
}

strictnull class B {
    public void meth();
}

public class TestClass {
    public static void main(String[] args) {
        A a = null;
        B b = null;

        a.meth();  // no-op
        b.meth();  // throws NullPointerException
    }
}
Comment by rodion.moiseev, Oct 23, 2009

@w...@beguelin.info rodion.moiseev: If I understand you correctly, you essentially want to replace null with Maybe<T>. So instead of a NullPointerException??, I would get a "Maybe<T> is undefined" exception at runtime.

Yes, I did mean: replace null with Maybe<T>, but this would imply a different behaviour as opposed to getting "Maybe<T> is undefined" at runtime. Consider this:

public Maybe<Integer> getValue(){
  if(hasValue()){
    return Maybe.Just.valueOf(value);
  }else{
    return Maybe.Nothing;
  }
}

public void main(){
  Maybe<Integer> value = getValue();
  
  //programmer is forced to check 
  //if the value is defined
  if(value instanceof Maybe.Just){
    Just<Integer> intValue = (Just<Integer>)value;
    //do something with the value
  }else{
    //do something else
  }
}

Of course, this is essentially the same as if(value == null){...}, the only difference is that the programmer is cannot use value before it is known to be defined. I know my example is very verbose (this is how I would write it in Java), but with some syntax sugaring I am sure it can be improved.

@ma...@mindcraftinc.com You have a valid point, but I think simply ignoring undefined/null values without throwing a NullPointerException is not a solution to the problem. On opposite it might make it more difficult to find bugs, because you code will seem to be working.

Comment by ma...@mindcraftinc.com, Oct 24, 2009

I had written in my Email: I am considering an alternative. I am not sure if this is a good idea, I am looking for intelligent feedback and I think that this is the forum to get it. After reading rdion.moiseev's Email and considering the merits of the NullPointerException?, here is my amended proposal. As with last time, I am looking for intelligent feedback on the merits of the proposal.

The problem we are trying to solve is the abuse of null as an object value.

  • An object starts in an undefined (or unset) state, until it is set. (undefined is the new null.)
  • Undefined objects must be set before being used
  • Objects can be tested with x.isSet()
  • Once set, an object cannot be unset
  • The following is invalid:
  • a = null;      // There is no null
    a = undefined; // Undefined is not a keyword
  • This is how it would behave:
  • if (a) ...;            // Throws a UndefinedObjectException
    if (a.isSet()) ...;    // True iff a is undefined
    
    Person c, d, e, f;
    String s;
    
    c.isSet();                     // False
    c = new Person("Couturier");
    c.isSet();                     // True
    
    d = c;                         // Now d === c
    d.isSet();                     // True
    c.setName("Khayyat");
    d.getName();                   // "Khayyat"
    
    s = e.getName();               // Throws UndefinedObjectException
    f = e;                         // Throws UndefinedObjectException
    
    d.setName("Tarzi");
    c = new Person("Tailor");      // c is now different from d
    d.getName();                   // "Tarzi"
    c.getName();                   // "Tailor"
Comment by rodion.moiseev, Oct 25, 2009

@ma...@mindcraftinc.com your amended approach is better, though the only improvement you seem to have made from the original null behaviour, is rid of null keyword and restricted reassignment of null-values (as in d = c; //Throws UndefinedException when c is undefined).

If the language is to allow nulls in the first place, then your approach is probably the best since it minimises the time the object is in the null-state.

Personally I believe there is no need for undefined or even the implied default object concept to exist in the first place, because it will cause abuse. For instance, the default for int is 0, the default for Integer is null, that is just confusing and when abused will result in different errors. The non-trivial cases of null usage can be covered by what was suggested by masklinn (Oct 06, 2009), memory leaks pointed out by rosti.bsd can be covered by a cleverer compiler or a del keyword to explicitly free the object when no longer needed (of course, del var would also imply that scope of var ends here). I realise there are plenty more non-trivial cases, like what to do with transient fields after deserialization, etc. but there must be an elegant solution to those too.

What would be useful, however, is an intended undefined value (note, not undefined by default, but by intention), such as in the example below:

  public class RoomFullOfPeople{
   /**
    * @return the age of the youngest person, or Nothing<Integer>
    *         if there are no persons in the room.
    */
    public Maybe<Integer> getYoungestPersonsAge(){ ... }
  }

In such case an undefined value (represented by Nothing<Integer>) might be a more elegant solution compared to returning -1 or throwing a RoomIsEmptyException. For such cases an intended undefined value (preferably with type information) would be a useful feature.

Anyway, for now the best thing to avoid null pointers is to use @NonNull?/@Nullable annotations in combination with nullity verification tools such as FindBugs?. Works perfectly!

Comment by ma...@mindcraftinc.com, Oct 27, 2009

I believe that an undefined state is beneficial. It is not the same as a default value (which I agree leads to errors) because it's not a usable state. When a variable is undefined, the only thing that can be done to it is to define it. It allow us to write code like:

Metal m[] = new Metal[300];
m[0] = new Metal("Hydrogen");
// Initialize m[1] to m[299] from a file containing traditional metal names.

The alternative is to load the metal names into a temporary collection, then initialize m from it, but this is boilerplate and awkward. We should allow variables to be declared and initialized on one line, we also need to allow them to be declared and initialized separately.

I don't mind abandoning the idea if someone can suggest a reasonable way to declare and initialize a collection.

Comment by christianedwardgruber, Oct 27, 2009

So I'm going to weigh in on this stuff, as one of the authors. Bobby and I have looked at this in more depth, and in the context of this paper ( ), and have decided to re-cast this somewhat. I'll be putting up a new proposal to replace this one sometime this week. It will focus on null-safety and will ensure that every dereference will either be null-safe, or explicitly null-tolerant, or disallowed by the compiler, but with some conveniences that will allow for convenient ignoring of nulls (in a safe way) without too much boilerplate. It may or may not still have the null object behaviour, for those occasions where null is tolerated, but we'll see. Anyway, you'll all be able to gab about it on the new wiki page.

Comment by masklinn, Nov 01, 2009

@rosti.bsd > Here is a simple Java code example of a recursion method that throws the OutOfMemoryError?? when the b = null line is disabled.

So you create a completely artificial and mostly nonsensical boundary making sure things are going to blow up with a non-deterministic GC. Is it supposed to prove anything?

> Nevertheless, it appears that there may be legitimate uses for null.

No. There are legitimate uses for the concept of a name pointing to "nothing", that doesn't mean null is a requirement, or even useful. Please, look up the concept of option types (described above, several times). They have all the advantages of null and none of the inconvenients.

> I know my example is very verbose (this is how I would write it in Java), but with some syntax sugaring I am sure it can be improved.

I don't think that's a good idea. Using null values should stay verbose. If you want to lower verbosity, consider lifting operations instead (making operations execute within the context of Maybe, avoiding the whole testing thing but retrieving a value still boxed into a Maybe rather than an unboxed one).

Null/maybe should be avoided, and making their usage verbose is a good way to lead programmers towards avoiding them.

Comment by rosti.bsd, Nov 01, 2009

masklinn once said:

@rosti.bsd > Here is a simple Java code example of a recursion method that throws the OutOfMemoryError? when the b = null line is disabled.
So you create a completely artificial and mostly nonsensical boundary making sure things are going to blow up with a non-deterministic GC. Is it supposed to prove anything?

It's not artifical, it's just an example of an intermediate object used in a recursion. If you know how to write a recursion and never use any intermediate object with references to them on stack, please explain how. Previously you wrote: "references to objects disappear when they're out of scope". Did you mean Noop, in comparison with Java, should have an extended definition of a local variable declaration scope?

This is how it is defined in Java specification: http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.4.2

The scope of a local variable declaration in a block is the rest of the block in which the declaration appears, starting with its own initializer and including any further declarators to the right in the local variable declaration statement.