Mario has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

No more excuses to use null references in Java 8

10.08.2012
| 45458 views |
  • submit to reddit
Tony Hoare introduced null references in ALGOL W back in 1965 “simply because it was so easy to implement”. After many years he regretted his decision calling it "my billion dollar mistake".

Unfortunately the vast majority of the languages created in the last decades have been built with the same wrong design decision so language designers and software engineers started to look for workarounds to avoid the infamous NullPointerException.

Functional languages like Haskell or Scala structurally resolve this problem by wrapping the nullable values in an Option/Maybe monad. Other imperative languages like Groovy introduced a null-safe dereferencing operator (?. operator) to safely navigate values that could be potentially null. A similar feature has been proposed (and then discarded) as part of the project Coin in Java 7.

Honestly I don't miss a null safe dereferencing operator in Java even because I can imagine that the majority of developers would start abusing it "just in case". Moreover, since the upcoming Java 8 will have lambda expressions, it will be straightforward to implement an Option monad that, as I hope to show in the remaining part of the post, is a far more powerful and flexible construct. 

I don't want to delve in category theory and explain what a monad is, even because there are already tons of very good articles doing this. My purpose is to quickly implement an Option monad using the Java 8 lambda expression syntax and then show how to use it with a very practical example. In Scala, a monad M is any class having the following 3 methods:
def map[B](f: A => B): M[B]
def flatMap[B](f: A => M[B]): M[B]
def filter(p: A => Boolean): M[A] 
In particular you can think to an Option monad as a wrapper around a, possibly absent, value. So an Option of a generic type A could be define as it follows:
import java.util.functions.Predicate;
public abstract class Option<A> {

    public static final None NONE = new None();

    public abstract <B> Option<B> map(Func1<A, B> f);

    public abstract <B> Option<B> flatMap(Func1<A, Option<B>> f);

    public abstract Option<A> filter(Predicate<? super A> predicate);

    public abstract A getOrElse(A def);

    public static <A> Some<A> some(A value) {
        return new Some(value);
    }

    public static <A> None<A> none() {
        return NONE;
    }

    public static <A> Option<A> asOption(A value) {
        if (value == null) return none(); else return some(value);
    }
} 
I also added some convenient factory methods for the Some and None concrete implementations of Option that I will implement later. Here Predicate is a single method interface defined in the new java.util.functions package:
public interface Predicate<T> {
    boolean test(T t);
}
that is used to determine if the input object matches a given criteria, while Func1 is another single method interface:
public interface Func1<A1, R> {
    R apply(A1 arg1);
} 
that I defined to represent a more generic function of one argument of type A1 returning a result of type R.

The abstract class Option has then two concrete implementations, one representing the absence of a value (something that we are used to wrongly model with the infamous null reference):
public class None<A> extends Option<A> {

    None() { }

    @Override
    public <B> Option<B> map(Func1<A, B> f) {
        return NONE;
    }

    @Override
    public <B> Option<B> flatMap(Func1<A, Option<B>> f) {
        return NONE;
    }

    @Override
    public Option<A> filter(Predicate<? super A> predicate) {
        return NONE;
    }

    @Override
    public A getOrElse(A def) {
        return def;
    }
}
and the other wrapping an actually existing value:
public class Some<A> extends Option<A> {

    private final A value;

    Some(A value) {
        this.value = value;
    }

    @Override
    public <B> Option<B> map(Func1<A, B> f) {
        return some(f.apply(value));
    }

    @Override
    public <B> Option<B> flatMap(Func1<A, Option<B>> f) {
        return f.apply(value);
    }

    @Override
    public Option<A> filter(Predicate<? super A> predicate) {
        if (predicate.test(value)) return this; else return None.NONE;
    }

    @Override
    public A getOrElse(A def) {
        return value;
    }
} 
Now, to try to put the Option at work with a concrete example, let's suppose we have a Map<String, String> representing a set of named parameters with the corresponding values. We want to develop the method
int readPositiveIntParam(Map<String, String> params, String name) { // TODO ... }
that, if the value associated with a given key is a String representing a positive integer returns that integer, but returns zero in all other case. In other words we want the following test to pass:
@Test
public void testMap() {
    Map<String, String> param = new HashMap<String, String>();
    param.put("a", "5");
    param.put("b", "true");
    param.put("c", "-3");

    // the value of the key "a" is a String representing a positive int so return it
    assertEquals(5, readPositiveIntParam(param, "a"));

    // returns zero since the value of the key "b" is not an int
    assertEquals(0, readPositiveIntParam(param, "b"));

    // returns zero since the value of the key "c" is an int but it is negative
    assertEquals(0, readPositiveIntParam(param, "c"));

    // returns zero since there is no key "d" in the map
    assertEquals(0, readPositiveIntParam(param, "d"));
}
If we couldn't rely on our Option we should accomplish this task with something similar to this: 
int readPositiveIntParam(Map<String, String> params, String name) { 
	String value = params.get(name);
	if (value == null) return 0;
	int i = 0;
	try {
		i = Integer.parseInt(value);
	} catch (NumberFormatException nfe) { }
	if (i < 0) return 0;
	return i;
}
too many conditional branches and returning points, isn't it? Using the Option monad we can achieve the same result with a single fluent statement:
int readPositiveIntParam(Map<String, String> params, String name) {
    return asOption(params.get(name))
            .flatMap(FunctionUtils::stringToInt)
            .filter(i -> i > 0)
            .getOrElse(0);
}
where we used an helper static method FunctionUtils.stringToInt() as a function literal, with the :: syntax also introduced in Java 8, defined as:
import static Option.*;
public class FunctionUtils {
    public static Option<Integer> stringToInt(String s) {
        try {
            return some(Integer.parseInt(s));
        } catch (NumberFormatException nfe) {
            return none();
        }
    }
}
This methods tries to convert a String in an int and, if it can't, it returns the None Option. Note that we could also define this behavior inline, while invoking the flatMap() method, using an anonymous lambda expression, but my advice is to develop a small library of utility functions, as I started doing here, in order to leverage the grater reusability allowed by functional programming. I think the comparison of the two readPositiveIntParam methods I provided illustrates well how the extensive use of the Option monad can finally allow us to write completely NullPointerException free software and, more in general, how a bigger employment of functional programming can dramatically reduce its cyclomatic complexity.
Published at DZone with permission of its author, Mario Fusco.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Loren Kratzke replied on Wed, 2012/10/10 - 2:17pm

I am personally not impressed with functional programming for my projects but admit that it can do some interesting things. One of which is to make code more complex and less readable while trying to make it less complex and more readable. Just my personal opinion, but I am well set in my ways.

I think making readable code is a skill and an art. I would rewrite your example method above as follows:

 int readPositiveIntParam(Map<String, String> params, String name) {
int i = 0;
String val = params.get(name);
if (val != null) {
try {
i = Integer.parseInt(val);
} catch (NumberFormatException nfe) {}
}
return Math.max(i, 0);
}

I find this to be much more readable that your original example and its one-line replacement.

  • Same number of lines
  • One "if" instead of two
  • One return statement instead of 3
  • Does not use bracketless "if" statements
  • Easy to grok in a debugger
But I am old fashioned. Not knocking functional programming, but I really, really like core Java without the black magic.

Stefan Zobel replied on Wed, 2012/10/10 - 2:49pm

Hi Mario,

nice post. A minor nitpick though: IMHO, Option should be constrained to have only 2 subtypes (Some and None) which is not guaranteed in your implementation. Tony Morris has also blogged on this:

Maybe in Java

Greetings,
Stefan

Mario Fusco replied on Wed, 2012/10/10 - 3:21pm in response to: Loren Kratzke

This is a very common objection. In my opinion readability is something subjective and highly influenced by what you're used to. Of course somebody who develops in Java (or C/C++) since more than a dozen years (like myself) has null checks and try/catch in his blood while he is not very used to read or write a lambda expression.

The only objective, measureable difference between my code and yours is the cyclomatic complexity. My code has a single path of execution while your has 3 different possible branches. This is the real big advantage of functional programming.

Mario Fusco replied on Wed, 2012/10/10 - 3:22pm in response to: Stefan Zobel

Very interesting, Thanks for the link.

Man Dev replied on Wed, 2012/10/10 - 5:43pm

And without if, null and catch :

import org.apache.commons.lang.math.NumberUtils ;
int readPositiveIntParam(Map<String, String> params, String name) {
return Math.max(NumberUtils.toInt(params.get(name),0),0);
}

Jed Wesley-Smith replied on Wed, 2012/10/10 - 5:47pm in response to: Loren Kratzke

Actually, your example is much more complex than the functional version - which is the point of the post. What happens if you forget to check for null? What happens if the NumberFormatException isn't checked (a feature that is so useful, no other language has copied it)? Your code will fall over.

Now we know that you as a professional old software developer would never make this mistake – but think of the children. They will make these mistake regularly, and they do.

The Option based version doesn't let you accidentally dereference it (modulo having a non-total get method which this version doesn't). It doesn't let you accidentally forget to catch an exception, or force you to catch one either. It is harder to make mistakes in Mario's version, and it is easier to verify correctness.

> Not knocking functional programming,

of course you are.

> but I really, really like core Java without the black magic.

Right, you actually don't understand it and so label it black magic. It isn't magic, it is a very simple and useful technique to eliminate what are common programming pitfalls. It has been battle-tested and proven to be superior to the alternative you are peddling.

Peter Levart replied on Thu, 2012/10/11 - 10:20am in response to: Loren Kratzke

@Loren

 I won't argue about readability, because it's in part a subjective category, dependent on one's background. I would just like to note that a part of your "revrite" was to replace:

  if (i < 0) return 0;
  return i; 
 

with:

  return Math.max(i, 0);
 

which is actualy a transformation of logic from imperative to functional. So don't say you dislike functional. It's all about balance.

;-)

 

Peter

Michal Margiel replied on Thu, 2012/10/11 - 3:29pm

Hello,

Thanks for your article,  is really nice. It is second time when I hear/read about Maybe Monad and again I have same impression. This *monad* looks just  like a fance name for Null Object Pattern, is there any REALL difference between those two contepts?

Mario Fusco replied on Fri, 2012/10/12 - 2:20am in response to: Michal Margiel

There are two answers to your questions: a (very) short one and a long one.The short one is: try to rewrite the example in my article usnig the Null Object Pattern and see what happens :) The long one follows.

First of all there's a clear difference under an implementative point of view. For what I know there are 2 different ways to implement the Null Object Pattern. The first one is to add a Null Object to all the classes of your business model (possibly including String, Integer and so on), and as you can imagine this solution is extremely verbose. The second is to try to implement it in a more generic way, for instance using a proxy as I suggested here. By the way I wrote that article about 3 years ago and now I am in total disagreement with it: it's amazing to see how experience can radically change your points of view. Howeverit it adds more complexitty to your application and doesn't work with final classes, so you still have the problem of how to deal with Strings and Integers.

In this sense you can think to the Option/Maybe monad as a glorified and generalized Null Object that works out of the box with all the classes of your domain. But there is more and, for example, you can give another quick look at the flatMap method of my Option (and how I used it) to figure out what. You can pass to it another function that transforms the value wrapped by the Option (if any) not in a differnt value, but in another Option wrapping a different value. And you could want to do that because even this second function, the trasforming one, could possibly return the absence of a value as my stringToInt method does.

In a word the Option monad gives you a degree of composability, that is made evident by the fluency in how you can use it, that cannot be reproduced using the Null Object Pattern.

Loren Kratzke replied on Mon, 2012/10/15 - 3:58pm in response to: Jed Wesley-Smith

 What happens if you forget to check for null?

I intentionally did not check for null because there is no point to doing so in this method.

What happens if the NumberFormatException isn't checked?

The same thing that happens if the author "forgets" to check for the same in his util library I suppose. But it is incorrect to say that my code will "fall over". It returns the same result as the author's code with the exception of a null key in which case my code could potenially return a non-zero result (if there is a value mapped to null).

I have been programming for 30 years (16 of those in Java) so probably understand far more than you give me credit for. I thought I made it clear that I personally do not have a use for functional programming. I understand it just fine but generally prefer not to use it.

I thought it was important to point out that if you are going to throw some code out there for A/B comparison then you should make sure the code is well written otherwise it undermines the credibility of the comparison. That said, my 10 lines provides the same result as the autor's 50+1 line alternative in this example.

Loren Kratzke replied on Mon, 2012/10/15 - 4:16pm in response to: Peter Levart

Hmm, I would call it more "functionish" than functional but I get your point.

oxbow_ lakes replied on Tue, 2012/10/16 - 5:39am in response to: Loren Kratzke

Loren - you are using Java and have been for a long time. You would probably agree that you like statically-typed languages: it means the compiler can reason more fully about the correctness of your program.

Setting aside for a minute whether Option is functional or otherwise, it encodes the property "this value might not exist" into the type system. Using Option means the compiler can reason more fully about the correctness of your program thus avoiding runtime exceptions. Which is the point of a statically-typed language, isn't it?

Having a similar experience arc to Mario (>10 years Java followed by > 3 years scala), I can only say that the use of Option has transformed my code from brittle and unreadable to clear and bulletproof. Looking at your code, I can certainly see what it is doing on a micro-level (i.e. step-by-step) but I cannot look at the expression as a whole and easily see what is going on - because of the cyclomatic complexity that Mario mentioned. The intention of Mario's code is much, much more obvious at the casual glance.

 

 

 

Mario Fusco replied on Tue, 2012/10/16 - 5:54am in response to: Loren Kratzke

The fact that you think to have the choice of not using functional programming, emphasises that no, you don't understand it.

Erwin Mueller replied on Tue, 2012/10/16 - 6:31pm

Can please somebody explain to me why there is so much fuss about NPE and null references/variables? For me it's very easy: null means: that variable/reference is unset. And a NPE is simply a bug in your code.

Why do you need a "workaround" for a bug in your code? If the program encounters a bug it should terminate gracefully. If you have a NPE, that means you have not set an important reference and that means your program is buggy.

For example, your example. You catch a NumberFormatException (which is a RuntimeException!) only so you can return a "the user does not entered a number" value. Why didn't you just test that the user have entered a valid number at the GUI level and inform the user that he/she have to enter a number? Also we have JFormattedTextField so the user cannot enter an invalid string!

I repeat myself: an NPE is a bug in your code, just like any other RuntimeException. You should not a) catch a RuntimeException and b) not workaround a NPE. Check your code and just eliminate null references.

It's very easy to eliminate null references. Just check your GUI so the user can enter only valid strings, use Null Object Pattern: http://en.wikipedia.org/wiki/Null_Object_pattern etc.

A null reference/variable means just one simple thing: it is unset (yet). I don't know if it's wise to allow reference/variable to be unset, that's a different question. But to "workaround" bugs in your code is definitely unwise.

Loren Kratzke replied on Tue, 2012/10/16 - 8:06pm in response to: Erwin Mueller

I so agree with you about NPE but was afraid to ask. I am getting beat up because I only have 30 years programming experience, am sitting on 1 million lines of code, work with 80 developers on a daily basis, but somehow I "don't understand" because I prefer 10 lines of verbose code instead of 50 lines of backing code to support one line of pretty code. I think this discussion is purely academic as real world concerns are not being addressed at all.

Loren Kratzke replied on Tue, 2012/10/16 - 8:26pm in response to: Man Dev

Awesome. That's some fine code golfing there. Hole in one!

Erwin Mueller replied on Tue, 2012/10/16 - 10:23pm in response to: Loren Kratzke

I think the worst mistake of Sun was to name all runtime exceptions "Exception". They should have used a better name, either "Bug" or "Error": RuntimeError. NullPointerBug, OutOfMemoryError, NumberFormatBug, etc.

Also I would have designed the exception classes hierarchy differently:

Error -> RuntimeError -> NullPointerError, OutOfMemoryError, NumberFormatError, etc.

Error -> Exception -> IOException, etc.

This whole mess of checked/unchecked exceptions is because developers think of runtime exceptions as "exceptions" and try to handle them like "exceptions", i.e. catch them, log them away and forget. Runtime exceptions are bugs in your code:

RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine.

Just rephrase it: RuntimeError is the superclass of those errors that can occur during the normal operation of the Java Virtual Machine.

[NullPointerException] Thrown when an application attempts to use null in a case where an object is required.

NullPointerError: Error that occures when an application attempts to use null in a case where an object is required.

See, Python is doing it right. Their runtime "exception" is called RuntimeError, and the Java NPE is called SyntaxError (SyntaxError is thrown in Python if tried access to a None type).

oxbow_ lakes replied on Wed, 2012/10/17 - 2:16am in response to: Erwin Mueller

Why do you need a "workaround" for a bug in your code? If the program encounters a bug it should terminate gracefully. If you have a NPE, that means you have not set an important reference and that means your program is buggy.

 

You are missing the point at length here. You want the compiler *help* you find the bugs in your code before you ship them. Thats why you use a statically typed language! The insane thing here is that you are actually arguing against a premise that your choice of a statically-typed language shows you actually agree with.

Using Option helps the following:

 

  • It aids maintainable, readable code by reducing cyclomatic complexity
  • It helps you prevent bugs from creeping into your code unnoticed by letting the compiler find them for you
  • It explains the behaviour of your classes/methods more fully to a consumer of your APIs (including yourself)

 

 

oxbow_ lakes replied on Wed, 2012/10/17 - 2:20am in response to: Loren Kratzke

I am getting beat up because I only have 30 years programming experience ...

I assume that by "beat up" you mean "am being engaged by various people with reasoned arguments" and it's not "because of" anything other than the fact that you are wrong.

 

Mario Fusco replied on Wed, 2012/10/17 - 2:25am in response to: Erwin Mueller

Can please somebody explain to me why there is so much fuss about NPE and null references/variables?
Because null references are the wrong way to model the absence of a value in a statically typed language. If also their inventor labeled them as his biggest mistake, don't you have at least the doubt that there could be something wrong in them.
For me it's very easy: null means: that variable/reference is unset. And a NPE is simply a bug in your code.
What it's easy for somebody could be difficult for somebody else and this mainly depends on your experience. After having developed in C/C++/Java for years and decades it's easy for you to reason on null references only because you are used to it. Try to ask to an Haskell developer if it is an easy concept. Moreover the main point here it is not if using null references is easy or not, but only if it is correct or not.
Why do you need a "workaround" for a bug in your code? If the program encounters a bug it should terminate gracefully. If you have a NPE, that means you have not set an important reference and that means your program is buggy.

Don't you prefer to structurally eliminate even the most remote possibility to get a NPE? Fix bugged code is far more expensive than write it correctly the very first time. I bet that despite it is so "easy" for you to use null references, sometimes, more often than not, you still encounter NPEs in your life as developer.

Why does it happen after so many years of experience if that is so easy as you say? In my opinion it happens because it is neither easy nor correct.

And what do you do when it happens? What is your solution? No, wasting time and money to fix the SAME bug again again is not the solution or at least not a solution I can/want afford. I want a structural solution that fixes the problem FOREVER.

For example, your example. You catch a NumberFormatException (which is a RuntimeException!) only so you can return a "the user does not entered a number" value. Why didn't you just test that the user have entered a valid number at the GUI level and inform the user that he/she have to enter a number? Also we have JFormattedTextField so the user cannot enter an invalid string!

What is the alternative for safely converting a String to an int in Java? I will tell you a secret: it doesn't exist only GUI development in Java. The String that you would like to convert in an int could come literally from wherever. You are right, I should not catch the NumberFormatException but only because the java method should not throw it. If it returned an Option<Integer> with Some(int) if it could do the conversion and None if it couldn't I shouldn't catch anything and use that result without worrying about it. This drivesme to another advantage of Options againts both null references and Excpetio: the firsts compose and can be used fluently, the seconds don't.

I repeat myself: an NPE is a bug in your code, just like any other RuntimeException. You should not a) catch a RuntimeException and b) not workaround a NPE. Check your code and just eliminate null references.

And I repeat myself: I don't want to fix the n-th NPE of my life. I want them to structurally disappear from my code forever.

It's very easy to eliminate null references. Just check your GUI so the user can enter only valid strings, use Null Object Pattern: http://en.wikipedia.org/wiki/Null_Object_pattern etc.

Ok, I got you do GUI develepment ;)

A null reference/variable means just one simple thing: it is unset (yet). I don't know if it's wise to allow reference/variable to be unset, that's a different question. But to "workaround" bugs in your code is definitely unwise.

No, a null reference just doesn't have any sensible meaning. It is only a black hole in your code, but differently from real black holes something actually raises from them: NPEs. A buch of them.

 

Loren Kratzke replied on Wed, 2012/10/17 - 1:00pm in response to: oxbow_ lakes

Wrong about expressing a preference, or wrong about liking clean code? It's a nice article and clearly the author is skilled and intelligent, but he failed to close the sale with me. I am just not impressed with any of this. Call me wrong if it makes you feel better.

Erwin Mueller replied on Wed, 2012/10/17 - 1:40pm in response to: Mario Fusco

Sorry I still think your Option type is a workaround of bad programming design and a workaround for buggy code.

What you are basically do is propose a default value for null references, that is not null. What you do is basically, you are hiding the fact that the user have entered an invalid value. In my opinion, if the user have entered an invalid value (a string that is not a number) the application should tell them that and ask that the user enter an valid value. This have nothing to do with null references or NPE. It's just good application design: validate the user input and make sure that your data contains only valid values.

What your Option type is doing is: if null or invalid integer, return default value, else return value.

But what if a value cannot have any meaningful default value?

The normal thing to do in my opinion is to tell the user that and ask for a valid value to be entered. If you have failed to do that, then you have a null reference in your code and a NPE is the result. But then it is clearly a bug in your application.

Tony Hoare basically agrees with me. He says that a new created reference needs to be assigned to a Null-Class (what is C#, Java, Python are doing) and the alternative is to have a language that forbids null references altogether or that have an additional qualifier that forbids Null-references. Something like public notnull String foo;

The String that you would like to convert in an int could come literally from wherever.

A string can't come from "wherever", it can come only from two sources: user input and an external source. User input needs to be validated and the external source either have already types (SQL database have integer type) or it lacks types (HTTP). But if it lacks type than it's like user input and needs to be validated. But again, that have nothing to do with Null.

Raging Infernoz replied on Wed, 2012/10/17 - 4:03pm in response to: Loren Kratzke

I'd re-write that as:
 
int readPositiveIntParam(Map<String, String> params, String name) {
  String val = params.get(name);
  if (val != null) {
    try {
      return Math.max(Integer.parseInt(val),0);
    } catch (NumberFormatException nfe) {}
  }
  return 0;
} 
Now just 9 code lines, only 2 returns, no pointless call to Math.max() for missing or non-numeric text; a doddle to read and much simpler.
 Not using null is a silly idea e.g. how do you cope with missing strings or optional values where you don't want or need the extra system expense of these silly Monad wrappers, including the extra garbage they will generate when found/generated values are returned rather than the none null substitute? Functional coding will only be useful if it cost effectively simplifies, speeds up execution, or improves code qualify, to justify the conceptual refactoring, otherwise it is pointless mental masturbation. 

Well written Java utility libraries demolish most of the arguments for functional code here, especially if you create your own optimised data structure classes.

I wanted genuinely usefull stuff like closures in Java 5..7, so see this functional junk as a pointless distraction; get the basics in place which genuinely simplifies code, makes it shorter and more readable; but keep it optional.  Java is not Scala, nor do I want it to be!

Raging Infernoz replied on Wed, 2012/10/17 - 4:53pm

The Throwable architecture is a mess of:

  Exception (checked)

  RuntimeException (unchecked)

  Error (unchecked)

There should be a fourth unchecked Issue base class for situations where this is less serious that a Runtime issue or Exception issue; all Throwables should support special Throwable Attributes so that you can customise how the compiler treats them for methods and code sections which use them. 

It really is ridiculous having to keep explicitly declaring thrown checked exceptions, or having to wrap exceptions in RuntimeExceptions, just because you don't need to declare or handle them in lower level code!

 

The support for Mutable Number and Mathematical operations is non existant to bare in Java, so these string parsing issues are the tip of the iceberg.  I've had to create several classes to handle checked/safe casting between number types, boxing, unboxing, string parsing which picks the best Number type, Mutable Number classes for use in Maps, and enum functions for unary numeric, binary numeric and comparator operators; this was a lot of work!  The lack of support for overriding operators is dumb, the issues could have been resolved using Annotations by now.

 

Mario Fusco replied on Thu, 2012/10/18 - 2:27am in response to: Raging Infernoz

Now just 9 code lines, only 2 returns, no pointless call to Math.max() for missing or non-numeric text; a doddle to read and much simpler.

9 lines of code vs. 1, 3 branch of execution vs. 1 and no it is not at all simpler neither to write nor to read.

Moreover, I don't understand why you and other people commenting this article are so focused on that particular example. I wanted to show a strategy to avoid the usage of null references and people here are wondering if the String comes from a GUI and who validated it or rewriting my example to demostrate how clever they are. When the finger points to the moon the stupid look at the finger.

Not using null is a silly idea

Debugging tons of NPEs and wasting time and money to workaround them filling you code with unreadable and unmaintanable null checks is a very clever one indeed.

e.g. how do you cope with missing strings or optional values where you don't want or need the extra system expense of these silly Monad wrappers, including the extra garbage they will generate when found/generated values are returned rather than the none null substitute? 

"Today object creation is cheap and mutation is expensive" - Brian Goetz

Functional coding will only be useful if it cost effectively simplifies, speeds up execution, or improves code qualify, to justify the conceptual refactoring, otherwise it is pointless mental masturbation. 

Options increase the code quality of a statically typed language like Java by modelling the possible absence of a value with a specific type. This enforces the management of the absent value at compile time instead of letting you discover it only at runtime with a NPE. Moreover Options improve the readability of your code and increase its maintainability by lowering the cyclomatic complexity. The only pointless masturbation here is fixing the n-th NPE of your life. Next time you get one, think again who is pointlessly masturbatimg himself.

Well written Java utility libraries demolish most of the arguments for functional code here, especially if you create your own optimised data structure classes.

Which one for example?

oxbow_ lakes replied on Thu, 2012/10/18 - 5:44am in response to: Erwin Mueller

What you do is basically, you are hiding the fact that the user have entered an invalid value. In my opinion, if the user have entered an invalid value (a string that is not a number) the application should tell them that and ask that the user enter an valid value
 

It's called defensive programming.  If I am exporting a an API method which takes a String, then I should not assume that the String is never null because of some faint hope that clients of my API should be checking that this is so before they pass it to me.

Whilst it is of course good practice to validate user input before it reaches the inner depths of your applicaiton, you should never, ever assume this has definitely happened when plumbing those depths.  

 

 

Loren Kratzke replied on Fri, 2012/10/19 - 6:27pm in response to: Mario Fusco

Don't you prefer to structurally eliminate even the most remote possibility to get a NPE?

 Answer: No. If I encounter an NPE it means that I have not properly taken into consideration the possibility that I could be handed "no" data and since I have not taken this into account, and it has happened, I have a bug that needs to be fixed. 

All you are doing is wrapping what would be an NPE into an object that states that it has no value. Now what are you going to do? One of three things:

  1. Check if you have "none" and branch accordingly (which is what you should be doing with a potentially null reference anyway), or
  2. Use the object. After all, isn't that what you are trying to do? Use an oject that has no value without getting an NPE? Why else would you go to so much trouble to avoid a NPE? How does your program benefit from this? Or,
  3. Assign a default value. What if there is no sensible default value? What if the nature of your data is entirely determined at runtime and you can't possibly imagine at dev time what default to assign? Do you instead use a unique dummy value that the rest of your program must check for so that it does not mistake the dummy value for a valid value? Or do you invent a NonePointerException if none is used?

Honestly, do you check for none? If so then why would you not check for null and skip all the bullshit?

All you are doing is moving the NPE problem/possibility into a different domain, and spending a lot of energy doing so, and gaining no net value.

Null is incredibly usefull. If you are getting NPE's all the time then you are a poor Java programmer and that is all there is to it.

And I repeat myself: I don't want to fix the n-th NPE of my life. I want them to structurally disappear from my code forever.

 You are fixing what you consider to be the problem (NPE) but you are only treating the symptom. The actual problem is that you are attempting to operate on data that does not exist.

Erwin Mueller replied on Sat, 2012/10/20 - 1:12am in response to: Loren Kratzke

The actual problem is that you are attempting to operate on data that does not exist.

Exactly my point.

But what is useful, is to check for invalid inputs early and throw NPE or IllegalArgumentException early on. I find the Validate class of Apache very useful. If you really want robustness I suggest to use the Validate utilities methods everywhere where your API expects to receive data.

Mark Hammons replied on Mon, 2012/12/03 - 11:29am in response to: Loren Kratzke

"Answer: No. If I encounter an NPE it means that I have not properly taken into consideration the possibility that I could be handed "no" data and since I have not taken this into account, and it has happened, I have a bug that needs to be fixed."

The elimination of null in lieu of an option or maybe type eliminates that issue. The data you take would either be Option<String>, which forces you to take into consideration the possibility of missing data, or String, which cannot have missing data when null is absent from a language.

"All you are doing is wrapping what would be an NPE into an object that states that it has no value. Now what are you going to do? One of three things:"

You can also pass along the Option<String> type while using map and flatmap to mutate the value it may or may not contain. The important part is that an Option type:

1. Forces you to deal with the possibility of no data.

2. Clearly documents in APIs that there is a possibility that your function may fail.

"Honestly, do you check for none? If so then why would you not check for null and skip all the bullshit?

All you are doing is moving the NPE problem/possibility into a different domain, and spending a lot of energy doing so, and gaining no net value."

Not always. It's not always needed. Like in the above function, I would do the following in scala:

value.map(_.toInt).getOrElse(0)

No need for branches or much else. 

"You are fixing what you consider to be the problem (NPE) but you are only treating the symptom. The actual problem is that you are attempting to operate on data that does not exist."

The fact that a data-type can actually not refer to data was a mistake. 

Henk De Boer replied on Mon, 2012/12/03 - 2:52pm

I like the example and the pipeline style of programming. In one way this style is, more or less, already used in Java and that's for the builder pattern. Okay, it's something different, but the idea of doing something in one flow is similar.

It also rimes pretty well with common best practices such as given by Uncle Bob in clean code; to return an empty list instead of null.

I do agree with especially Erwin, that this style of programming does carry a risk with it. If you're not careful, you really do obscure the cause of problems. In absence of no data (where not allowed), your code will simply keep running until all operations that you programmed are done and then things stop.

The result of the above is that simply "nothing has happened". This can be nice (don't bug the user or programmer with nasty errors), but you can actually be in a worse situation. The action that the user thought has happened, actually hasn't happened at all. Yet there is no error and no indication anywhere that something has gone wrong.

Imagine a hotel booking example. The user should have entered a city, but forgot it. That value is now null, but since we're functional advocated, we hate null and have None. All our still runs. We locate the hotel correctly (it returns another None, no error) and we book the None hotel "correctly" (e.g. our operation is a noop).

When the user actually arrives at the hotel, with the wife and children, he finds no room has been booked at all. The hotel is completely full, it's 11:00 PM and there are no other hotels nearby. I'm sure the user is not going to be happy, but hey, we didn't had to deal with a NPE!

I'm slightly exaggerating perhaps, but this stuff DOES happen in "null friendly" languages.

Again, I do like the pipeline and the functional pattern, it has advantages for readability (in my opinion, not everybody agrees), but you do have to be aware that None types, Empty Lists, and null safe operators (like in Groovy, or in JSF/JPS Expression Language) can obscure real errors. 




Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.