Quantities in VisualWorks
Cincom Smalltalk Engineer, Michael Lucas-Smith recently wrote:
In Cincom® VisualWorks® we’ve had a few arbitrary limits in Timestamp and Duration. Timestamp stops at milliseconds and there’s a need to go down to nanoseconds there for interfaces to Oracle and other databases. Duration is fixed to nanoseconds as of VW7.8 which covers the majority of short time period computations. But ultimately, we’d rather not have to go through a 10^-9 -> 10^-12 migration sometime next release.
So instead, we’re making Duration and Timestamp hold on to seconds at an arbitrary scale. Now, that’s not actually what this post is about; but it’s tangential. As part of the effort to “get this right” I wanted to understand the right way to internally represent Duration; as whatever we do there we’ll be replicating in Timestamp. I’d rather not get two classes wrong in VisualWorks 7.9.
To understand it better, I took a step back to look at the entire problem of representing quantities or measurements in Smalltalk and other programming languages. So let’s start with the best of breed for static languages: Boost.Units — now, I know some of you will jump out of your seats in fury and rage at this point because I’m not talking about your favourite functional language. Trust me when I say I understand and I’m sorry you just broke your favourite chair.
Boost.Units is a C++ library that provides a zero-runtime overhead for representing units in your C++ program. This is, honestly, a fantastic addition to the C++ language and should be in the base. If only they could get their compilation errors to make sense I’m sure we’d all be using it. After all, if a developer starts programming with inches in mind and accidentally collaborates with anyone outside of the USA, they’ll be combining centimetres with inches and bam, you crash your Mars satellite.
It’ll slow down your compilation time, but at least your program will be correct! This is one of the few times where types do not get in your way and save you a lot of time and (especially in the case of NASA) money.
On to the next best of breed — Measurements in the Cincom Public Repository. This library comes from two people, the first is Travis Griggs who is my colleague here at Cincom. The second is Ken Greene. Now I’m mentioning their names, particularly Ken, here because they came up with a way of representing units at runtime that has very low overhead on arithmetic operations. We’ll come back to what this is later in the post.
So with these two great examples of units and quantities to go off of, I set about trying different variations for describing quantities in VisualWorks generically. I wanted to allow it to blend with the existing (and future) implementation of Duration. I *really* wanted to know if there is any design impact on Duration that I need to know about now before I go and change its implementation in VisualWorks 7.9.
After going through many iterations, I ended up with a design using the same trick as Measurements, but with more capabilities, such as instantiating Duration when dealing with the Time dimension. Or instantiating an Angle when you’re dealing with radians, degrees and gradians. There is of course the Temperature class too – but I won’t bore you with the details of that one.
Because we’re creating and destroying objects constantly if we’re doing tight loop arithmetics on quantities, we need to make sure the implementation is only being dragged down by a lack of stack-allocation in an otherwise very smart virtual machine.
This is where Ken Greene comes in to the picture. When you multiply two quantities together, you also need to multiply the units together, eg: 5 seconds * 5 metres = 25 metres.seconds — to do this, each base unit is given a prime number. Derived units, such as metres.seconds are an integer that is composed of primes. This means you can decompose the integer and find out which base units make it up. You compute the name of a newly derived unit but you can also quickly look up previously computed units using the same integer quickly.
We have a class called Fraction in Smalltalk, which has two numbers (numerator and denominator) which can be used to describe the unit too, for example 5 seconds / 5 metres will create a fraction unit of seconds/metres.
The whole implementation ended up being two classes: Unit, ArithmeticQuantity (to mirror ArithmeticValue) and one exception: Incompatible. The package contains two specializations of ArithmeticQuantity: Angle and Temperature; but strictly speaking those domains could be removed from the library and it’d be just fine. The rest of the dimensions from SI units end up being generic. The library re-uses Duration to represent the Time dimension though.
You can find the library in the Cincom Public Store under the name Quantities and Quantities-Tests. I’ll be updating the library once the new Duration is integrated in to VisualWorks 7.9; so if you’re in the vw-dev program you’ll want to use the 7.9 version instead of the 7.8 version. The package contains a bunch of documentation, but only time will tell if the documentation is sufficient. It almost goes without saying that you can also use this package in Cincom® ObjectStudio® too (and Cincom® WebVelocity®!).