A few years ago I started exploring an idea I call “smart numbers”. In C we had pointers and numbers. In C++ we tend to use smart pointers instead of pointers. Why not smart numbers instead of numbers?
Why do we use “smart pointers”? Pointers in C are used for 4 things: pointer arithmetic to access arbitrary points in memory, pointers to objects, pointers to arrays, and pointers to objects inside arrays. Smart pointers separate these uses into separate types. We have
shared_ptr for pointing to objects;
valarray for pointing to arrays; and iterators for pointing to objects inside an array. Smart pointers also often deal with memory management, access patterns (like validity checking, bounds checking, locking, or logging), and copying policies (like copy-on-write).
Memory management and access patterns usually overshadow the separation of operations that smart pointers provide:
|pointer to object|
|pointer to array|
|pointer to object inside array|
The separation of operations prevents certain kinds of errors. For example, if I have a pointer to an array,
**p, it’s easy to accidentally treat this as an array of pointers by invoking
*p instead of
(*p). If I had used
ptr<vector<int> > p, then the compiler would tell me that
 is not a valid operation on
Things that are implemented the same way aren’t always used the same way. It sometimes makes sense to have separate types for the different uses, not only for different representations. (Object-oriented programming goes further and suggests using the same type for the same use even if it has different representations.)
In mathematics, vector algebra deals with points and vectors (not to be confused with pointers and
vector). A point is a location in space. A vector gives a direction and distance. The two are different, but related. They have the same representation: (x, y, z). In vector algebra, points and vectors are often confused. Operations on vectors are applied to points, which can be viewed as vectors from the origin. If we want to be strict, the operations are:
|p + p||not allowed!|
|p - p||v|
|p + v||p|
|p - v||p|
|v + v||v|
|v - v||v|
|p * k||not allowed!|
|v * k||v|
|p / k||not allowed!|
|v / k||v|
Keeping the two separate is nice from a conceptual standpoint, but sometimes it gets in the way, which may be why vector algebra texts sometimes ignore the distinction. For example, if you want the midpoint of
p2, you might want to write the code
(p1+p2)/2. In our strict type system, this is not allowed. You can neither add points to points nor can you divide points by 2. Instead, you have to rewrite this as
p1 + (p2-p1)/2.
In every programming language I know, numbers are int or real. There are variants on the size, like byte vs. short int vs. long int vs. bignum, or long double vs. float, but there are basically two types. Like pointers, the numeric types tell you about the representation and what operations work on that representation, but not the uses. If we have types based on uses, we’ll avoid a certain class of errors and introduce a little more structure into our programs.
Numbers from science often carry units like meters, seconds, grams. You can’t add 3 meters to 5 seconds. But in our programming languages, these would both be represented as a real, and you can add 3 + 5. if we had units for things in our code, the type system could catch the error. There are in fact libraries for units, like UNITS [PDF], Quantities, SIunits, and dimnum, Invariant. There’s also a more general one that can build new kinds of units, like meters18 per gram3-seconds4, but I can’t find the link to it.
What I haven’t seen before in a smart numbers library is an analogue of the points vs. vectors distinction. There should be a distinction between positions and differences. For example, what are the operations on a temperature? Temperatures cannot be added together. They can’t be multiplied or divided by a scalar. They can however be subtracted. But the difference between temperatures is not itself a temperature, but a change in temperature. You can add a temperature and a temperature change to get a new temperature. You can multiply or divide a temperature change by a scalar. If you list the operations on temperatures, you will find they are very much like operations on points, and the operations on temperature changes are very much like vectors.
Integers have a similar distinction. There are cardinals and ordinals. The cardinals are sizes, like 3. The ordinals are positions, like 3rd. It doesn’t make sense to add two ordinals (what is 3rd + 5th?) but it does make sense to add two cardinals (3 + 5). It also makes sense to add an ordinal and a cardinal (3rd + 4 = 7th).
We have three examples of this distinction: points/vectors, temperatures/changes, ordinals/cardinals. Do all numeric quantities fit into this pattern? I don’t know yet. While working on a game, I did write some code for building smart numbers. However I haven’t used it enough to know where it breaks down.