Scientific.Dimension: Type Arithmetic and Physical Units in Haskell

June 3, 2007

One of Haskell’s most outstanding features is it’s type-checker. Mis-matching types are a quick indication that something is wrong with a particular piece of code. And type annotations are useful documentation tool that provides hints about the uses of a function. Type checking is however not unique to programming languages. There is a similar concept in Physics: Units. Some programming languages, such as Mathematica provide built-in unit-checking. The Haskell language lends itself to this purpose very nicely, due to it’s type-checking system. As opposed to Mathematica, this can be done without changes to the core language itself.

This post will start out by introducing simple type arithmetic using Peano numerals and progresses to implementing a full unit checker on top of the Haskell language. The post is also available as a literate Haskell module.

The Glasgow Haskell Compiler (GHC) is used, as we will require the use of the -fglasgow-exts and -fallow-undecidable-instances extensions.

Arithmetic in the type-checker

In our unit-checking code, we will need the ability to add and subtract numbers in the type system. The numbers themselves are represented using Peano numerals. Positive Peano numerals are composed of two parts: Succ and Zero. In Haskell, their definition would look like this:

Where Zero represents the number “0”, and Succ is the “successor” of a number. For example, the type ‘Succ Zero’ is “1”, and ‘Succ (Succ (Succ Zero))’ is the number “3”. To add two numbers, we create a Haskell type class.

>class Add a b c | a b -> c where> add :: a -> b -> c

In the above class definition, the type variables ‘a’ and ‘b’ are the inputs to the calculation, and ‘c’ is the resulting output type. The “a b -> c” part tells the type-checker that the type-variable ‘c’ can be determined by knowing ‘a’ and ‘b’. In other words, a and b “determine” c. This means that for every possible case of ‘a’ and ‘b’, we will have to specify the resulting ‘c’. Here’s our first case:

Now, we need to cover the slightly more complicated case of adding any two positive numbers. This is done recursively: Given the positive numbers ‘Succ a’ and ‘Succ b’, we first calculate ‘c = a + b’, and then return ‘c + 2′. Note that ‘a’ is equal to ‘Succ a’ minus one, and that ‘c + 2′ is the same as ‘Succ (Succ c)’.

In the above instance declaration, everything before the ‘=>’ symbol is a precondition. Since we calculate ‘c = add a b’, we need to make sure that addition is defined on ‘a’ and ‘b’ with result-type ‘c’. This is the reason for the pre-condition.

In our unit-checking code, we will also need negative numbers. They are defined using ‘Prec’, where ‘Prec’ stands for “preceding”. For example, ‘Prec (Prec Zero)’ represents the number “-2″.

>-- Negative Peano numerals>data Prec n = Prec n

Of course, we will have to extend our definition of ‘Add’ to include negative numbers. The new instance declarations are very similar to the ones for positive numbers.

The type of ‘one’ is ‘Succ Zero’. This is determined at compile-time using our recursive class-definition for ‘Add’. We will also want to be able to subtract two numbers. The definition of Sub is almost identical to Add:

Changing the fixity of the Cons type-constructor will allow us to drop the parentheses.

>infixr 6 `Cons`

Using this definition of lists allows many functions to be implemented in a more type-safe manner than using Haskell lists. For example, the Prelude functions ‘head’ and ‘tail’ will fail when passed an empty list. If head was instead defined using our new definition for lists, the type-checker would ensure at compile-time that no empty lists are passed as arguments.

> safeHead :: x `Cons` xs -> x
> safeHead (x `Cons` xs)= x

Running something like ‘safeHead Nil’ will result in a type-error. Implementing functions such as ‘foldl’, ‘map’ and ‘concat’ is left as an exercise to the reader. (If you can’t figure it out, make sure to ask for help at #haskell on irc.freenode.org)

Unit Checking

We are now ready to start start implementing our unit-checker. Units are represented as a list of numbers that count how much of a certain “primitive” unit is contained in our unit. For example, suppose we use use the SI units Meter, Kilogram and Second as our primitive units:

As with the Add class, ‘a’ and ‘b’ are the two units multiplied together, and ‘c’ is the resulting unit. This time, any two units determine the third. As with the ‘Add’ class, we need to instantiate the ‘Mult’ class with every possible input.

It would be nice, if we could instantiate the Prelude Num class to define functions such as (+), (-) and (*) for our dimensions. However, multiplication in the Num class is only allowed on values of equal type. Thus, we need to define our own addition, subtraction, multiplication and division functions:

>(a `Dimension` b).*(c `Dimension` d)=(a * c)`Dimension`(mult b d)

>(a `Dimension` b)./(c `Dimension` d)=(a / c)`Dimension`(divide b d)

For addition and subtraction, we need to additionally make sure that the units of the two arguments are the same:

I’m aiming to provide a reasonably complete library with statically unit checking. The project site is http://code.google.com/p/dimensional/. I’m currently trying to wrap the library up for a 1.0 release.

It’s good to see that someone else is interested in this “problem”. While there are some significant differences (e.g. I’ve differentiated between units and “quantities” at the type level) I think we’ve tackled it similarly.

Feel free to take a look at my library. The main documentation is the extensively commented literate haskell code and given that you have solved the problem once it should be easy for you to follow. I imagine e.g. the support for powers and roots might interest you.

If your interest in this goes beyond a one-off academic exercise maybe you would be interested in contributing/collaborating? Thanks.