Summary
Introducing the CEDSimply project and some thoughts on a clean way to assign
pointers using safe multiple dereferences of possibly nil pointers: CurrentLogger = gRS.runner.UI.LogTo unless nil
Advertisement
I often see code like (slightly ugly contrived example):
if (gRS.runner!=0 && gRS.runner.UI!=0 && gRS.runner.UI.LogTo!=0)
CurrentLogger = gRS.runner.UI.LogTo
or, taking advantage of the c treatment of nil pointers as false:
if (gRS.runner && gRS.runner.UI && gRS.runner.UI.LogTo)
CurrentLogger = gRS.runner.UI.LogTo
In an abstract syntax form, this can be expressed as:
This is the kind of assumption, like having zeroed memory by default, that could be built into a language. Indeed, it might be nice to only be allowed to assign a nil pointer explicitly:
CurrentLogger = gRS.runner.UI.LogTo
or:
CurrentLogger = Nil
However, I keep being really really uncomfortable when a language does things that are going to subvert the instincts of a good c++ or Java programmer.
I find myself rather liking the simple form:
CurrentLogger = gRS.runner.UI.LogTo unless nil
it retains the original assignment statement in its normal location, read first.
the added nil protection is expressed succintly so would not be too noisy if repeated often.
I miss the Object Pascal with statement, dreadfully so when I first moved to C++.
Trying to write a nil-safe with leads to:
with gRS.runner.UI unless nil:
CurrentLogger = LogTo
Which doesn't really work for me:
the with statement no longer ends with the expression being dereferenced.
it doesn't say the same thing, we don't have a check of LogTo being nil, although sometimes we might not care.
I'm not sure there's a readable alternative, maybe some of these meet your fancy:
with unless nil gRS.runner.UI:
CurrentLogger = LogTo
with nil-safe gRS.runner.UI:
CurrentLogger = LogTo
with guaranteed gRS.runner.UI:
CurrentLogger = LogTo
safely with gRS.runner.UI:
CurrentLogger = LogTo
I came to Christopher Diggins' blog and Heron project because for a few years I've been dissatisfied with existing programming languages and how well they allow me and many others to communicate to a computer and other programmers.
CEDSimply is currently a theoretical project in establishing a clear vision for a language, it may end up as:
a set of idioms for using an existing programming language,
a DSL (Domain-Specific Language) or
as an interpreter and c++ Code Generator.
I have realised it might be a tiny bit ambitious to aim at developing the entire language and environment alone so it is going to have to be an open source project. Maybe I'll at least establish a BDFL-like reputation, rather than getting rich :-)
I've spent a lot of time in the Macintosh world working in the code generation space: writing code generators that target c++ frameworks such as Think Class Library (for Prototyper) and enhancing AppMaker from Bowers Development.
From publishing a Windows code generator and PowerPlant framework portability kit for AppMaker, I've now moved on to acquiring the product (Spec Bowers has retired to run cabins).
AppMaker has a JSP-like template environment for code generation, using its own OO language. This, Python and years of trying to write highly-usable C++ API's (OOFILE) were the seeds of my thinking in CEDSimply, watered with dabblings in Forth and Scheme.
try {
currentLogger = gRS.runner.UI.LogTo;
} catch ( NullPointerException notUsed ) {
// ignore or assign a default
}
However I would probably write class UI so that LogTo was private and get and set methods are used instead of field access. Then when a UI was created or setLogTo is called I can ensure that LogTo is set to a none-null-default value if null is given as the argument.
As an aside I have never actually written code that has any significant null testing in it. Probably because I tend to use get and set methods rather than field access.
To check the final pointer value, the one you're copying, I think this would have to be:
try {
if (gRS.runner.UI.LogTo != null)
currentLogger = gRS.runner.UI.LogTo;
} catch ( NullPointerException notUsed ) {
// ignore or assign a default
}
> However I would probably write class UI so > that LogTo was private and get and set > methods are used instead of field access.
In both REALbasic and C++ the above code could be using getter functions, not necessarily simple field access. You can't assume they are simple fields without more context.
REALbasic has for years allowed function invocation to drop empty parens and there are operator overloads to achieve similar in C++.
In the latest REALbasic you can also map properties with getter and setter functions.
> As an aside I have never actually written code that has > any significant null testing in it. Probably > because I tend to use get and set methods rather than > field access. Don't you write null checks inside some of those methods? :-)
The points you make above are correct. Perhaps if I flesh the example out a bit that will help (to keep it short I will just do UI - the same process is applicable to the other classes).
The point is that by using access methods the problem goes away. You don't need to test at the point of use that everything isn't null because it can't be null. The advantage of this technique are that:
1. The checking and resulting actions are well contained and documented in the classes that are resposible for the feature and not left to the user to deal with.
2. Generally there is only one point of assignment to a private field, the set method. Whereas the user code contains many reads using the getter and therefore encapsulating the null handelling saves a lot of effort.
Also, I'm not sure about the with construct. I find it a little confusing. Also, the scoping rules of such a construct are not obvious to me. I guess I would assume that the 'with' context would be narrower than the local context.
> Also, I'm not sure about the with construct. I find it a > little confusing. Also, the scoping rules of such a > construct are not obvious to me. I guess I would assume > that the 'with' context would be narrower than the local > context.
That's not really clear. I mean that the with scope hides the names in the surrounding scope in the case of collisions.
package nulltesting;
importstatic java.lang.System.*;
publicclass Test {
staticclass Wrapper { // the thing that has a field that could be null
final String wrapped;
Wrapper( final String value ) { wrapped = value; }
}
staticabstractclass Thunk< T > { // common class used many times
T eval( final T defVal ) {
try {
final T temp = f();
return temp == null ? defVal : temp;
} catch ( NullPointerException e ) {
return defVal;
}
}
abstract T f();
}
staticfinal Wrapper defVal = new Wrapper( "No" ); // default for wrappers that don't exist
static String exists( final Wrapper wrapper ) { // a wrapper exists method, other overloaded methods for other wrapper types
returnnew Thunk< String >() {
public String f() { return wrapper.wrapped; }
}.eval( defVal.wrapped );
}
publicstaticvoid main( final String[] notUsed) {
final Wrapper ok = new Wrapper( "Yes" );
final Wrapper null1 = new Wrapper( null );
final Wrapper null2 = null;
out.println( "ok exists? " + exists( ok ) ); // prints Yes
out.println( "null1 exists? " + exists( null1 ) ); // prints No
out.println( "null2 exists? " + exists( null2 ) ); // prints No
}
}
That only works for classes I write, correct? I could create a solution using reflection but I agree with the author that a language level syntax for this could be useful, albeit in limited circumstances.
I agree with some of the comments. I think the usefulness of the unless construct is doubtful. It avoids a NullPointerException, yes, but in many cases you would have to code something by hand anyway in the case of a null argument. I have found that in many cases, it is possible to deal with null pointers in a general way. Some idioms that I have found useful:
- Coding getters to never return null, as Howard suggested. A frequent usage is in struts with objects (usually Strings or Lists) that are bound to JSP pages via bean:define tag. If it is null, the page just refuses to display with a mysterious error message. No problem if the getter method is coded appropriately.
- In many cases, you want to compare a String but it may be null. I have seen a lot of code like
if (myString!= null) { if (myString.equals("something"){ ... } else if (myString.equals("that"){ ... } else "){ ... } }
This is crazy. I just write myString = notNull(myString); , where notNull() returns myString==null? "": myString .
- It is often worthwile to rewrite standard methods in order to accept an object that can be null. E.g. boolean equals( Object o1, Object o2) instead of o1.equals(o2) saves you a lot of null checks.
When coding to JDBC, you often need close statements in finally clauses but you can't be sure whether the connection is initialized at all. Instead of writing endless null checks thousands of time in the code, use a generic close() method:
protected static final void closeDBResource(Object resource) { if (resource==null) return; try { if (resource instanceof ResultSet) { ((ResultSet) resource).close(); } else if (resource instanceof Statement) { ((Statement) resource).close(); } else if (resource instanceof Connection) { ((Connection) resource).close(); } else { throw new IllegalArgumentException("Invalid resource class: " + resource.getClass().getName()); } } catch (SQLException e) { staticLog.info( "Exception in closeDBResource(): " + e); } }
A further benefit of this approach is that the exception handling is also encapsulated. In this case, it is appropriate to swallow the exception, otherwise you'll have to catch it in the finally handler which is simply awkward.
I use analogous methods for rollback (exceptions are swallowed) and commit (exceptions are propagated) so that I can write very clean code without ever having to check for null pointers.
> I agree with some of the comments. I think the usefulness > of the unless construct is doubtful. It avoids a > NullPointerException, yes, but in many cases you would > have to code something by hand anyway in the case of a > null argument. I have found that in many cases, it is > possible to deal with null pointers in a general way. Some > idioms that I have found useful: > > - Coding getters to never return null, as Howard > suggested. A frequent usage is in struts with objects > (usually Strings or Lists) that are bound to JSP pages via > bean:define tag. If it is null, the page just refuses to > display with a mysterious error message. No problem if the > getter method is coded appropriately. > > - In many cases, you want to compare a String but it may > be null. I have seen a lot of code like > >
> if (myString!= null) { > if (myString.equals("something"){ > ... > } else if (myString.equals("that"){ > ... > } else "){ > ... > } > }
> > This is crazy. I just write
A simple solution that solves at least this specific example is:
if ("something".equals(myString){
...
} elseif ("that".equals(myString){
...
"A simple solution that solves at least this specific example..." That's right. However, I find this idiom a little less intuitive. Also, you can use the notNull() idiom to make existing code null-safe in just one line instead of ahving to check several statements.
I think the Java designers could have done us a great favour by providing a few simple null-safe methods in the standard library, like
static boolean Collection.isEmpty( Collection c) { return c == null || c.isEmpty(); }
static List List.notNull( List l) { // less generic, but most often needed return l == null ? new ArrayList() : l; }
Having to write such methods oneself is a minor hassle but I feel Java code quality would generally benefit from having them in the standard. I see a lot of code cluttered with null checks that could so easily be avoided.