Question

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?

Était-ce utile?

La solution

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.

Autres conseils

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.

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.

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.

Licencié sous: CC-BY-SA avec attribution
scroll top