1

Related to this question i want to know if there is a concise way to eleminate null values out of code in general or if there is not.

E.g. imagine a class that represents a user with birthday as attribute which can be set but does not have to, which then means it's null. In scala and similar languages one could use the Option type to describe this and i see why this is better then just having a regular null value as one would code it in java.

However i wonder if this is the right concept or if we can do it better in this case and even in general.

I came to the idea that we could replace the users birthday (and other optional or possibly unknown/unset attributes) with a list of possible optional attributes. So we could have like

ourUser.getOptionalAttributes()
// could return a list [Date Birthday, Float Height, Color Eyecolor]
// or just [Color Eyecolor]
// or even an empty list

However using a regular list would mean that we could store multiple birthdays e.g. So we needed a way to tell the compiler that the list may only contain one Birthday type.

Am i totally on the wrong track here and are option types the (philosophical) correct way to express optional attributes? Also, are there additional concepts for handling the logic of optional attributes other than nullable types and option-like types?

8
  • 1
    The list idea is good, but you need some usability enhancements. Add a Get(Attribute) and Has(Attribute). And we'll use the birthday a lot, so add a GetBirthday and HasBirthday. Actually, those two calls aren't atomic and could cause issues, so just have GetBirthday, but have it return a list with either a Birthday or no elements. Actually, checking for no elements feels a little hacky, so always use the same empty list for your nothings, and call it Nothing. Hang on, you've invented Option :) You're actually kinda on the right lines, Option/Maybe types are best viewed as collections
    – Phoshi
    Commented May 2, 2014 at 9:26
  • Why not then throw an exception if Get(...) is called if the attribute has not been set yet? This way we don't have to return an empty list when get(...) is called. Seems more straightforward to me, no?
    – valenterry
    Commented May 2, 2014 at 9:55
  • You could do that, but you run the same risk in multi-threaded environments. I ask Has(Birthday) and get yes, a fraction of a second before the user's request to remove their birthday goes through. I call Get(Birthday) and have an exception thrown in my face. Whoops.
    – Phoshi
    Commented May 2, 2014 at 10:04
  • Shouldn't we then make both requests {has(...); get(...)} somehow atomic like when working with databases?
    – valenterry
    Commented May 2, 2014 at 10:07
  • Good luck! What if you have a large has(x) block, must all of that then be atomic? The performance hit would be appreciable. Returning either the value you want or some special sentinel value is a much saner way of doing it, because you sidestep that entire problem. Optional is an improvement over null because it has more useful semantics, unlike null which has the semantics of "will crash your application if you do anything with it".
    – Phoshi
    Commented May 2, 2014 at 10:46

4 Answers 4

1

What is a type?

A type is meta data that can be used, at compile time or run time, to describe the shape of our data.

trait A { val b: X; def c(y: Y): Z}

So we know in our program that when ever we see an a: A, we know that a has the shape of A so we can call a.b safely.

So what shape does null have?

null has no data. null does not have a shape. String s = null is allowed as null has no shape, so it can fit any where. MikeFHay above says that an empty collection can be thought of as a null object. This is wrong, an empty collection has exactly the same shape as any other collection.

If a field can be null, then we can no longer rely on a.b to be safe. The shape of the data in this field is not truly A, only potentially A. The other shape it can be is null. That is the shape of this field is a union of A and null.

Most languages allow unions to be implemented as a tagged union. Scala's Option type is a tagged union of None | Some(a:A). Like wise Ceylon adds a ? postfix for types that say that the type may be null as in String? s = null.

2

are option types the (philosophical) correct way to express optional attributes?

The repeated word suggests that yes, they are. They simply and clearly express what you're trying to express.

There is one null-alternative that you haven't mentioned, and can often be better than a traditional null or Option. The Null Object Pattern, where you define an object with neutral default behaviour, so that calls to it do nothing transparently. An empty collection can be considered a null object, but the concept can be applied in other places too.

2
  • 1
    In which way is the Null Object Pattern better than an Option type?
    – Andres F.
    Commented Aug 6, 2014 at 23:19
  • @Andres Huh, good point, I guess a proper monadic Option is sort of a generalised Null Object. Although sticking with the empty Collection example, it's easier to define the many different behaviours of contains, isEmpty, size etc. in one place than to do (in Java 8) maybeCollection.map(Collection::contains(x)).orElse(false) and such all over the place. For simpler, smaller interfaces though, I suppose Option makes the Null Object pattern redundant.
    – MikeFHay
    Commented Aug 7, 2014 at 10:22
2

So we could have like

ourUser.getOptionalAttributes()
// could return a list [Date Birthday, Float Height, Color Eyecolor]
// or just [Color Eyecolor]
// or even an empty list

Am i totally on the wrong track here and are option types the (philosophical) correct way to express optional attributes?

The only time using a list would make sense is when a particular attribute may occur 0 to n > 1 times. I don't even know what you plan to do with the first example - that list is heterogeneous. (I'm not saying it's not possible - you could use case classes so that each list element holds either a Date, a Float, or a Color, but seriously, what does that get you?)

A Maybe/Option type is fundamentally the right thing to represent either Nothing or a single value of type T. If type T has n distinct values, Option <T> has n + 1 distinct values; and being a distinct type from T, it's an error to blindly assign an Option <T> to a T in the same way that it's wrong to blindly assign a 64-bit number to a 32-bit number.

For signaling failure in a computation, you could alternatively throw an exception instead of returning an Option. This is useful when a failure is likely to be handled higher up the call stack. It's also useful when passing functions to other functions. For example, you could have a function A that wants a function B that returns an integer. It may be the case that B could theoretically fail to produce an integer, but you've ensured that this particular use of B will produce a result. Having Option in the return type of B would make it incompatible with A.

Also, are there additional concepts for handling the logic of optional attributes other than nullable types and option-like types?

There's the concept of dividing values of your type into the appropriate case classes. As a simple example, suppose you're working with cartesian coordinates and need to work with both rectangular and polar coordinates. Although there's two representations, it's important to understand that they both represent values of the same type. A naive way of implementing this would be:

public class Point2D {
   public static enum Type { RECT, POLAR }
   public Type type;
   public Option<Double> x; // Rectangular coords
   public Option<Double> y; // Rectangular coords
   public Option<Double> angle; // Polar coords
   public Option<Double> magnitude; // Polar coords
}

But this approach is wrong - it's not that every field (save for type) is optional, it's that there's two kinds of Point2Ds and you've conflated the two. If you modeled this correctly using case classes, none of the fields would be optional:

abstract class Point2D
case class Rect(x: double, y: double) extends Point2D
case class Polar(angle: double, magnitude: double) extends Point2D

This is more related to choosing the right model than it is to "missing" values, but I mention it since it can lead to the wrong use of Option (or null, god forbid.)

As others have mentioned, there's the concept of the Null Object Pattern, but that's tied to the specific semantics of the type. A null object doesn't make sense for some types.

1

I believe it's a question of what type of question you would like to pose in the end. Lets say you just want all related information to show up whenever you show someone the user. Then a list (or EnumMap, if you want to make some uniqueness guarantees) would be nice since you just want to iterate over it and print the stuff.

However, if you want to ask questions like "if this user have a birthday show a notice 1 day in advance to friends" then even if you start of with the list you are left with a Option in the end. I don't think this is bad, but it doesn't free you from the idea of optional values.

-edit-

A drawback is that you have to fit all your data into the same type. Might be easy or difficult depending on your requirements.

5
  • That's not bad! I wonder why i didn't think of maps. So this will guarantee we only have one birthday saved for each user. Only drawback is that the compiler still can't help us with e.g. creating getter and maybe setter for entries in the map but only for the map itself. But having a map for these optional types seems like a good way not having to use nulls or similar. =)
    – valenterry
    Commented May 2, 2014 at 9:56
  • 1
    @valenterry Don't use a map for things that are properly part of the type you're modeling. You'd be erasing type information. Think of what would happen if you took every type in your system and replaced all its fields with a single map indexed by keys. Now how do you know at compile time what fields a type should have?
    – Doval
    Commented May 2, 2014 at 11:57
  • @Doval I'd of course only do this for types that could have null values as their value is maybe missing (by business logic). And of course this would be "working around the language" as a language could also support this kind of optional attributes right? To me that would be the same as using a map as function parameter if the language does not support optionale function parameters.
    – valenterry
    Commented May 2, 2014 at 12:10
  • @valenterry There's still the potential pitfall of the map accepting additional parameters that aren't meant to be part of the function's signature. I would prefer to just declare multiple versions of the function with different number of arguments.
    – Doval
    Commented May 2, 2014 at 12:15
  • Yes but i can check that. If I use multiple methods i maybe end up with a dozen methods all calling the same method. That's maybe better but still only a workaround and only possible if you can overload methods.
    – valenterry
    Commented May 2, 2014 at 13:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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