An open-source scalar package and associated software tools have been developed in the Scala programming language, including plotting tools based on the free GRACE plotting package. The scalar package represents physical scalars and can help to prevent errors involving physical units in engineering and scientific computation. The scalar package includes a complete implementation of the standard SI metric system of units and many common non-metric units. The design also allows users to easily define a specialized or reduced set of physical units for any particular application or domain. The scalar package can be used in two different modes: one mode provides unit compatibility checking but is slower, and the other mode bypasses the compatibility checks but is much faster and still prevents the most common type of unit error. Switching between the two modes requires no changes in the user’s code, making it convenient and usable with no significant performance penalty for even the most computationally intensive applications.
The checking overhead would not be necessary if it could be left to the compiler. There have been attempts in this direction with Scala typeclasses if I recall correctly, but I think these had limitations.
Over 2 decades ago I made a Java compiler with support of dimensional checking; during compilation it erased the information. It worked fine and without run time overhead, but with some limitations; e.g., you could not make arrays with elements of varying dimensionality.
Shortly afterwards Grant Petty published a Fortran library for dimension checking without that limitation, but with the run time overhead that you may then expect. Full flexibility without runtime cost may be impossible to achieve.
On a high level, formulas in physics are not really about units; they are more about the physical dimensions such as distance, time, speed, acceleration. I miss that level of abstraction a bit in your approach. My extension supported definitions such as
Instead of syntax in the latter line I would now prefer phrases such as Double[Speed] or Speed[Double]. I think that typeclasses could do most of the work that the dimension checking part of my compiler did, but just not all of it. Maybe a relative small change in the Scala compiler could change that.
Thanks, Andre. My approach to the problem is a spinoff of my work in air traffic control automation. It bothered me that scalar physical quantities are normally represented as numbers without explicit units. I’ve seen the errors where minutes get mistaken for seconds, degrees for radians and such. Sometimes those errors aren’t detected for a long time – like after a research paper is published – whoops! (I’m referring to a colleague here, of course, not me!) I originally developed my approach in Python, then I reimplemented it in Scala several years ago.
That is some interesting work you did 20 years ago. I skimmed your paper and may take a closer look at it later. It would be nice to have phusical dimensions represented as types and compatibility checks at compile time, but that is very difficult and beyond my level of expertise.
Jesper Nordenberg had that in his Metascala package a few years ago. As brilliant as it was how he used the Scala type system, however, his approach left a lot to be desired. Even though the unit compatibility checks were done by the compiler, the runtime performance was still very slow (by a factor of over 20 IIRC). Moreover, the actual physical units seemed to be a secret known only to the compiler. I couldn’t figure out how to get it to print unit information.
My approach has limitations, but I’m sure you will agree that it is preferable to raw dimensionless numbers.
My approach does not stop the user from defining multiple base units for length, for example. This could actually be considered a feature, I guess. (At one point I thought about defining separate length units for horizontal distance and altitude, since they are rarely combined in ATC, but I never actually did it.)
One of the most common errors in engineering and scientific computation involves mistaking degrees for radians. Using my scheme, one would write sin(30 * deg) to get the sin of 30 degrees. That is of course just a convenient way to do the standard conversion that is routinely done – but often forgotten. A while back, I decided that the trig functions should reject any argument that is not explicitly converted to radians. But radians are dimensionless, and trying to enforce a named unit for it caused other problems (relating to the implicit conversion of a Double to a dimensionless Scalar). I eventually abandoned that effort to force the user to explicitly convert the trig arguments to radians.
In any case, let me know if you have any other suggestions or ideas.
And so on. I typed this in directly. I’m sure there are errors. I hope this gets the general idea across. Types, functors/mutators, monads, simple “*” methods for construction. This will be fast enough for everything less than super computing problems. Depending on the codes, it gives a place to optimize if need be. Move everything out of the type traits and into the final classes, that kind of thing.
Your Angle class sounds interesting. Where can I find the code of find out more about it?
Correct me if I am wrong, but I was under the impression that ANY user-defined type will be much slower than the builtin float type (i.e., “Double”). That is why my approach is to just swap my Scalar class for Double for performance. The trick is that the “swap” does not require any change to user code. It is done by swapping one source file and recompiling, or swapping a jar file.
As I explained earlier, I wanted to shadow and wrap the basic trig functions with my own equivalent functions that force the user to explicitly declare radians. So for example, sin(0.35) would throw an exception, but sin(0.35 * rad) would compute the sin of 0.35 radians. And sin(35 * deg) would compute the sin of 35 degrees, of course. That would catch many degree/radian errors. But my implementation caused other problems with implicit conversion from Double to Scalar, so I abandoned it.
Again, I would like to see your Angle class. Thanks.
I can’t find the code back of my C# Angle class, but it was pretty straightforward.
In Scala you could have such a class with the scalar angle in radians.
Methods sin, cos, tan, toDegrees, toRadians are then straightforward.
A companion object would then contain asin, acos, atan, atan2, fromDegrees, fromRadians.
Angle would thus be a wrapper so it has a run time overhead.
Maybe you could program it in such a way that this wrapper will be erased.
I would like to see that if anyone manages to make it.
The dimensionality checking system in my Java compiler only worked with primitive numeric types; not with boxed numbers. It applied erasure, like is done with List[double], so without any performance penalty.
You would typically write double*Distance, but double[Distance] might have been nicer.
I don’t understand what this class accomplishes. As far as I can tell, the “toRadians” argument might as well just be called x. What am I missing? How does this help to prevent the user from forgetting to convert from degrees to radians? Thanks.
It’s what André was describing, unless I misunderstood him. It helps the user by providing an API which works with Angle instead of Double. The only way to construct an Angle is by calling fromRadians or fromDegrees (the Angle constructer itself should have been private of course, I fixed this now) which accomplishes more or less the same thing as the a * Degrees or a * Radians API.
Thanks for pointing out squants. I think I saw it a while back, but I just reviewed the documentation again. Here is my take on it.
It is a nice design and has the advantage of compile-time checking of unit compatibility. However, I’ll bet it is much slower than plain Douibles (by a factor of 10 to 20 or more). I would have to do some testing to confirm that, but I don’t have time for that. If I am right, then squants is useful only for applications that don’t require much speed. That severely limits its applicability.
The designers of squants could possibly do what I did and make a “Doubles” version that can be swapped for the regular version (by swapping a jar file or by swapping one source file and recompiling). I don’t know if that is possible with their design, but if it is I think it would be well worth doing because it would extend the range of applicability to computationally intensive applications.
Other than that, I prefer my syntax to theirs because it is more natural, but that’s just a personal preference.
Thanks for that information. Yes, I am aware of value classes and opaque types, but it is not yet clear to me how or whether they can be used for this problem. If anyone knows, please pass it along. Thanks.
FYI, I have upgraded this project to use Scala 2.13.0.
In the process, I also realized that some of my earlier discussion in this thread was not quite right. I had said that I tried and failed to come up with a scheme to prevent errors involving passing angles in degrees to trig functions that take them in radians. I should give myself more credit. After reviewing my code, I realized that I do have a method for that. In the full-checking mode, if you try to pass an angle as a standard Double (or Float), it will throw an exception.
For example, sin(23.5) will throw an Exception. To calculate the sin of 23.5 degrees, one would use sin(23.5*deg). For an angle that is already scaled in radians, one would pass it as a Scalar explicitly in radians, such as sin(0.24*rad) for example. This requirement forces the user to explicitly specify degrees or radians, thus preventing potential errors that have been known to go unnoticed for extended periods of time.
I also added a short section in the user guide to explain this feature.
The proper way to view these plots is with your pdf viewer in a mode in which you can step one full page at a time using an arrow key or the page-down key (rather than scrolling). Hope you find them interesting.