DEV Community

jaredforrest
jaredforrest

Posted on

Null Safety: Type-Level Strategies

What is null?

The concept of null in programming originated from the need to represent the absence of a value or an uninitialized state. The idea can be traced back to the early days of programming languages, such as ALGOL 60 and its successors like C and C++, which introduced the concept of a null pointer. A null pointer is a special value that points to no memory location or object.

In modern programming languages, null continues to be used as a representation of a missing or undefined value. It is commonly employed in various contexts, such as variable initialization, function returns, and object references. When a variable is assigned a null value, it means that it does not currently hold any meaningful data. This allows developers to explicitly indicate the absence of an object and handle it accordingly in their code.

Why is it a problem?

I'll start with a quote by Tony Hoare, the person who created the null reference.

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object-oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

The reason this is a problem is because just by looking at it you don't know if an variable is null. So when you try to use it an exception may be thrown. One way around this is to check every variable when you use it if it is null. But this would very quickly become tedious and wasteful because most of the time you know the variable cannot be null.

Union Types

There is another way around it though, having a different type for objects which may be null. Let's say we have a function which could return an object with User type but may also return null. In typescript, we can create a union type to show that. Let's look at an example:

function getUser(id: number): User | null {
  if(id < users.length){
    return users[id]
  } else {
    return null
  }
}
Enter fullscreen mode Exit fullscreen mode

We have a function which gets a user by their id if they exist, otherwise, it returns null. If we try to use user we may get an exception Now if we try to use user typescript will give an error.

const user = getUser(100);
console.log(user.id);
// ❌ 'user' is possibly 'null'.
Enter fullscreen mode Exit fullscreen mode

So we are forced to handle the case when user is null.

const user = getUser(100);
if(user !== null){
  console.log(user.id);
}
Enter fullscreen mode Exit fullscreen mode

In this case, it seems pretty obvious that user could be null but as applications get more complex, it gets harder to keep track until eventually, you get a bug which may be hard to pinpoint.

Option Types

This is not the only way to do things. In some programming languages, they do away with null completely and have a type known as the option type or maybe type. This type is used to encapsulate an optional value, meaning it represents a value that may or may not be present. It is used in mostly the same way you'd use the union

The option type consists of a constructor that can be one of two values, empty (usually called None or Nothing) or holding the original type T (often written as Just T or Some T).

For example, Rust implements the Option type, so the example above would look like this:

function getUser(id: usize): Option<User> {
  if id < users.len() {
    return Some(users[id]);
  } else {
    return None;
  }
}
Enter fullscreen mode Exit fullscreen mode

TIP: A usize is a positive integer used to index lists.

and to use it

let user = getUser(100);
if let Some(user) = user {
  println!("{user.id}");
}
Enter fullscreen mode Exit fullscreen mode

The option type provides a more explicit and safer way to handle the absence of a value compared to using null. By using an option type, you are forced to handle both cases: when the value is present and when it is absent. This leads to more robust and reliable code, as it helps prevent null reference errors and encourages developers to handle all possible scenarios.

Conclusion

Null can be a problem in programming because it introduces the possibility of null reference errors and makes code more error-prone. By using option types or similar constructs, developers can address these issues and write more reliable and maintainable code. If you're looking to expand your knowledge there are also other ways to ensure null safety like null coalescing or optional chaining.

Top comments (0)