2

I've been interested to start using the Null Object / Option Type approach for replacing my old habits of null-checking multiple parts of my code. But, from the many examples I saw out there, it seems it's a pattern used in conjunction with generics.

Great for C#, Java, Haxe and a bunch of others I'm sure.

NOT so great for ActionScript 3.0 (which is what I was intending to use it for).

So, am I under the wrong impression that this won't be possible / worthwhile in a language that doesn't support Generics? Or is there a way to pull it off without them?

NOTE: It seems the "Null Object" pattern shown in this PatternCraft tutorial doesn't quite resemble the way Option Type was described in some of the other discussions in this stackexchange site. His way looks more like a factory class that spits out a Null??? derivative of some base-class. I was hoping there could be a way to create Null-Objects without needing to write a dedicated class for every single case scenarios where a null-check would typically be used.


EDIT: My apologies, I didn't realize "Null Object" and "Option Type" were two completely different beasts. For the sake of keeping the answers and feedback relevant to the question though, I only changed the title (was originally How to use Null Object / Option Type Pattern in a language that doesn't support generics?).

  • 1
    References to other questions that inspired this one: programmers.stackexchange.com/a/12836/9387 – bigp Aug 8 '14 at 2:01
  • 1
    guava-libraries (in Google Code wiki) explaining it well: code.google.com/p/guava-libraries/wiki/… – bigp Aug 8 '14 at 2:12
  • There's no way to implement Option/Optional/Maybe generically without generics. Null Object pattern is something else, that's simply when you provide an object with sane default or no-op behavior. – Doval Aug 8 '14 at 2:20
  • Null object pattern and option types are two completely different things. – Euphoric Aug 8 '14 at 5:15
  • 2
    I would go even further and say that Null Object and Maybe are in some sense exact opposites: Maybe forces the client to explicitly deal with a potentially absent value, whereas Null Object makes the client completely ignorant about the fact that a value might be missing. – Jörg W Mittag Aug 8 '14 at 15:18
3

There is major difference between Null Object pattern and Option types.

Null Object is pure OOP (thus no generics) pattern, where one object implements abstract class / interface in such a way, that calling the method through the interface is same as not calling the method at all. That means the caller has no idea that type of instance is a Null object. So these two cases are equivalent:

// without null object
IInterface obj = null;
if(obj != null)
    obj.method()

// with null object
IInterface obj = new NullObject();
obj.method()

Option types on the other hand are type-safe way to ensure the caller of the method knows that there is possibility that variable might contain "no valid value". This gives compiler power to either warn or error when programmer tries to call method on variable, that might contain null.

[PSEUDOCODE (that looks like C#)]
// case A
Option<IInterface> obj = getObject();
obj.Method() // ERROR, possible "Null reference exception"

// case B
Option<IInterface> obj = getObject();
if (obj.HasValue)
    obj.Method() // OK, compiler knows that obj has valid value thanks to the if

But I believe option types don't make much sense in language, that doesn't have them as language feature (eg. most OOP languages), because compiler is not implemented as to detect such things as the first case (unless you use some kind of static code correctness checking). So you gain nothing against normal null check. Null object pattern on the other hand is natural in all OOP languages. Even those without generics.

Good example of OOP language having support of Option type is C#' Nullable. You know value types always have valid value and Nullable allows you to specify cases where variable might not contain valid value. But this is only for value types.

  • If I'm understanding correctly then, the Option Type (in languages that truly supports it) is treated with a bit more attention from the compiler to be certain the Method or Value you're trying to access has been if()'ed checked first? I can see that would be a huge advantage to have over having to manually test a null-check at runtime. – bigp Aug 8 '14 at 12:56
  • 1
    Case B is not how option types work; you unbox the value and then call the method on it. The statement that they don't make sense unless the language has built-in support isn't true, though it certainly helps. – Doval Aug 8 '14 at 12:56
  • @Doval I found that odd too. @Euphoric, isn't the Option's "wrapped" object supposed to be obtained through obj.Value and then call the Method() on that value? – bigp Aug 8 '14 at 12:59
  • 1
    @bigp An Option type isn't checked through ifs. You'd provide a function to execute in case it doesn't have a value, and a function to execute if it does. E.g. option.match(empty: () => "Nothing here!", present: (value) => value.ToString()). The unboxing would be done by the match method if and only if a value is actually there; the client code can't make any mistakes. There's no need for the compiler to get involved and check for something like if (optional.HasValue()) { optional.GetValue() }. – Doval Aug 8 '14 at 13:03
  • @Doval I do agree that my explanation is kind of weird. But the point is that there should be no other way to access the value inside Option, other than the typesafe way. Which would probably be hard in most OOP languages. – Euphoric Aug 8 '14 at 16:25
3

Option is isomorphic to a collection which can only ever be empty or contain a single element. So, you implement it the same way you would any other collection.

If you can implement a list, set, map, tree, heap, stack, array, trie, treap etc. in your language, then you can implement Option in exactly the same way.

See AS3Commons Collections for a comprehensive collections framework implemented in ActionScript. (Note: it doesn't seem to be as powerful as Scala's, but it can at least compare favorably with Java's.) In essence, without parametric polymorphism, you lose type-safety, the same way you do with Java 1.1, .NET 1.0, C, Pascal, Modula, etc.

3

The purpose of option types is to let the compiler know exactly where a value may be empty in a language where null simply does not exist (typical for any language that supports algebraic data types). This way, the compiler can give compiler-time errors if you forget to check for emptyness. Here's an example in Rust, an imperative language with a C-like syntax:

let my_number : Option<int> = read_number();
return 2 * my_number;          // compile error

This won't compile because my_number does not contain an int but an Option<int> (i.e. an int that may be empty). The compiler does not allow multiplication on Option<int>.

Therefore, it is necessary to pattern match my_number in order to extract its value:

let my_number : Option<int> = read_number();
match (my_number) {
    Some(my_actual_number) =>
        return Some(2 * my_actual_number);  // ok
    None ->
        return None;                        // fallback if it's empty

The pattern matching forces the programmer to handle every possible scenario: in this case, the None case. This is how it prevents the typical "forgot to check for null" problem.

However, in a language where null is pervasive, option types aren't nearly as useful since everything can already be null anyway. Therefore the compiler can't really help you (since inferring the nullability would require solving the halting problem).


The null object pattern not the same thing, but it is a related concept.

In languages that have first-class support for option types, one is permitted to apply an operation to the internal object inside the option type, if it exists. It serves as a shortcut for the common activity of "do X to the object, but if it's empty then forget about it". This process is typically called "mapping" and this operation can be defined for all option types.

The map operation takes an option object (Option<T>), as well as a function that can act on the internal object (T -> R), and produces the result of the function wrapped in an option object (Option<R>).

In fact, the example above can be rewritten much more succinctly using a map combined with an anonymous function:

let my_number : Option<int> = read_number();
return my_number.map(
    // use anonymous function to specify what to do
    |my_actual_number| 2 * my_actual_number
);

Map can be thought of as a recipe that:

  • If the option object is not empty, then apply the provided function and return the result in a Some(...).
  • If the option object is in fact empty, then do nothing and just return None.

In the null object pattern, one uses a "null" class that possesses same methods as the non-null class (they could be siblings in the inheritance hierarchy), but arranged in such a way that the operations on the "null" class simply do nothing. Hence, the null object pattern is similar in that it accomplishes this same goal, albeit in a different, more object-oriented/duck-typing way.

  • In this case, using option types just adds unnecessary syntactic burden on the users since it provides no benefit. Not really. If you consistently use an option type in your own code, you only need to null check when interacting with 3rd party code. That's way better than any and every variable being potentially null. Case in point, F# has an Option type even though if you call other .NET code you might get a null. – Doval Aug 8 '14 at 18:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.