Nathan Kleyn

Scala by day — Rust, Ruby and Haskell by night. I hail from London.

Hi! I currently work for Townhouse as the Head of Software Development, writing Typescript by day, and Rust and Ruby by night. I write for SitePoint about Ruby, and I’m on Twitter and on GitHub. You can get in touch with me at mail/at/nathankleyn.com.


Top and Bottom

06 Sep 2018

As a follow-up to my previous post on Unit vs null, I thought it might be useful to talk about some of the other special types we have in statically typed languages.

There are actually two other special types in the type-system that you may be interested in: namely, the bottom and the top type.

The Bottom Type

The bottom type (written as Nothing in Scala) is a type that is impossible to create. It is commonly used as a placeholder when a type isn’t known to the compiler. See for example what happens when we don’t tell the compiler the types of the keys and values in an empty Map:

scala> Map.empty
res0: scala.collection.immutable.Map[Nothing,Nothing] = Map()

As it has no other information to go on, the types for both end up as Nothing.

It’s also useful for saying that a function never returns — for example, a function that exits the program before returning could be written as:

def doSomething(): Nothing = sys.exit(1)

It should be clear from the type that it is impossible for this function to produce a return value. In fact, it’s not even possible to write a function that has a real return value if you have Nothing as the return types:

scala> def doSomething(): Nothing = 123
<console>:11: error: type mismatch;
 found   : Int(123)
 required: Nothing
       def doSomething(): Nothing = 123

You therefore know that upon seeing a function returning Nothing, it will definitely never return if you call it — all guranteed because it’s impossible to make a value of type Nothing!

The Top Type

The opposite of the bottom type is the top type. Also called the universal type (and written in Scala as Any), this is simply a way to say “this could be any possible type”. For example:

def doSomething(): Any = 123

This function actually returns an Int but typing it Any works — what’s going on here? This is because Any is actually the ancestor of all types in Scala, even Object:

scala> val x: Object = new Object {}
x: Object = $anon$1@b428830

scala> x.isInstanceOf[Any]
res1: Boolean = true

You’ll often see Any used by the compiler when it is trying to fill in a missing type (so called “type inference”) but couldn’t find anything more specific to choose.

Why Do We Need These Types?

The reason both bottom and top types have to exist comes down to how static-typing works — compilers of statically typed languages are, fundamentally, trying to solve two problems:

The bottom type is a natural way to represent an impossible scenario during the first of these tasks. The bottom type is the default type if a type cannot be inferred — that is, when you don’t annotate a type, Nothing will be the end result if the compiler cannot find another more specific type after searching through everything else.

The top type is the default for constraint reduction. It gives a compiler a final ultimate result if nothing can be proven when doing constraint reduction. Any is what you end up with when two types constrain each other so much that they have no other ancestor in common, and works because every single type has Any at least in common.