Towards Null-Safe Groovy

Currently I am implementing some experimental features from which some of them hopefully make it into GContracts next major release. One of the issue I've been playing with is support for null-safe aka void-safe code. The main idea is to let GContracts provide compile-time checks for null-safe code parts. Letting it be 'code parts' only is the first restriction which we will have to introduce. We all know null is evil, but: null is not the new goto. There are use-cases where assigning null to a variable might still be appropriate. Think of a double-linked list data-structure, where null could be needed to mark the first previous and the last next reference. However, the use of null more often leads to errors whenever a method call is executed on a variable which has been assigned to null. In order to provide compile-time checking of instance variables, local variables and parameters I added a new annotation to GContracts annotation package: @NullSafe:
 
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
public @interface NullSafe {}
 
In addition, I added the NullSafeASTTransformation which is supposed to do the compile-time checking stuff. To do that, we need a set of rules which enforce null-safety for variables annotated with @NullSafe:

Local Variables, Parameters

Whenever a local variable or a parameter is marked as null-safe, only assignments to are allowed. The AST transformation done so far checks for local variables marked with @NullSafe and takes care of valid assignments. Whenever an assignment does not conform to the rules stated above, a compile-time error is thrown. E.g. the following source code snippet fails during compilation:
 
package tests

import org.gcontracts.annotations.*

class A {

  def some_operation(@NullSafe def param1)  {
     def bla = "test"
     param1 = bla
  }
}

BUG! exception in phase 'semantic analysis' in source unit 'script1281039992077748355994.groovy' param1 must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:62)
 
The same applies for local variables:
 
package tests

import org.gcontracts.annotations.*

class A {

  def some_operation()  {
     @NullSafe def bla = "test"
     def otherBla = "test2"

     bla = otherBla
  }
}

package tests

import org.gcontracts.annotations.*

class A {

  def some_operation()  {
     @NullSafe def bla = "test"
     def otherBla = "test2"

     bla = otherBla
  }
}

BUG! exception in phase 'semantic analysis' in source unit 'script12810425282531878012710.groovy' bla must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:31)
 
Notice, the last code snippet won't compile according to the rules defined above, as an assignment of a not-null safe variable to a null-safe variable is not valid. If we want to pass compile-time checks, we simply had to annotate otherBla as @NullSafe.

Instance Variables, Fields

Instance variables need to be assigned to a non-null value whenever the construction of the according object is finished, thus the object complies to the explicit class invariant. Whenever a method is called an assignment to null would trigger a compile-time failure:
 
package tests

import org.gcontracts.annotations.*

class A {

  @NullSafe String myProp

  def A()  {
    myProp = "test"
  }

  def some_op() { myProp = null }
}

BUG! exception in phase 'semantic analysis' in source unit 'script12810407257201736681757.groovy' myProp must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:59)
 

Assignments from Non-Void Method Calls

In fact, this is only half the story. Whenever a variable is assigned to the return value of a method, we have no simple way to check at compile-time whether that method returns null. A possible solution for solving this scenario in terms of keeping code null-safe is to specify variable default values via annotation closures (btw: here starts the part I actually did not implement by now, so don't get confused with the annotation class above):
 
package tests

import org.gcontracts.annotations.*

class A {

  def some_op() {
      @NullSafe({ "empty" }) def i = "aloha"

      i = some_function()
      // at this point, i will be "empty" not null!
  }

  def some_function() { return null }
}
 
The default value is assigned only when an assignment to the null-safe variable would result in a null reference. Annotation closures would provide great flexibility to define arbitrary complex initialization code:
 

class Address {

    @NullSafe({ new City(name: 'dummy') }) City city
    // ...
}

 
An alternative approach would be to annotate methods as @NullSafe, which would avoid calling methods not being marked with @NullSafe in null-safe assignments.

What's next?

That's it with my experimental @NullSafe feature. I know that this is a naive first approach, but at least its a starting point and I would really appreciate your feedback on this topic either in this article's comment section or at the Groovy-Dev mailing list [1]. It would be very important to know whether this method works or if it is not applicable in real-world projects from your point of view. One issue is, that although GContracts uses AST transformations, it is not an integral part of the programming language, which could result in e.g. meta- or reflection-calls which make null-safe variables null etc. Just imagine Hibernate injecting null values into @NullSave instance variables - levering null-safe variables immediately . In fact, this is the point I am not completely clear about - does it make sense to introduce such a feature at library/AST transformation level?

[0] GContracts - Project Home Page
[1] Groovy-Dev mailing list @ nabble