Pragmatic aspects of dimension types, and the problem of angles

Dimension types are extremely useful for preventing errors in programs that manipulate physical quantities. There are several Haskell libraries for them, F# has a built-in version, etc. (I've read about a nice Python package that addresses the same issues dynamically, which may be a good source of inspiration, though my personal interest is in static approaches.)

Of course there is the thorny issue of selecting a basis with which to express dimensions. The dimensions of the SI base units are a common choice. The dimensions of the cgs units are another appealing choice in some respects, though you do need to deal with fractional powers (e.g. 1 esu = 1 g^1/2 cm^3/2 s^-1, so the dimension of a charge is Mass^1/2 Distance^3/2 Time^-1, whereas in the SI system it is Current^1 Time^1). There is also some appeal to allowing the creation of additional user-defined basic dimensions in addition, to allow dimensions like Vehicle^1 Time^-1 for the rate at which traffic is flowing.

Since my target users are engineers, I am inclined to choose the dimensions of the SI base units. For simplicity I will set aside the question of additional user-defined basic dimensions, because it is only slightly related to my primary question.

There is broad agreement (for decent reasons) that angles are dimensionless, and there is a long history of mostly treating them that way in the mathematics, physics, and engineering literature. Yet pragmatically, serious errors can arise when revolutions are mistaken for radians or angular velocities are confused with frequencies. Factors of 2 pi are liberally sprinkled all over the place, and it is pretty easy to forget or misplace one.

This form of confusion and traditional resort to name-based disciplines seems very much like the type of confusion and lack of formality that dimension types are intended to address. I find that I much prefer my toy system where angles are treated as dimensions to the version in which they are not, because it requires me to annotate all the places where numbers turn into angles (and vice versa) with the "units" that I am intending to use. This naturally leads to extremely good documentation of interfaces, serialization formats, and interoperation with external code.

There is a related problem where torques and energies share the same SI dimension (Mass^1 Distance^2 Time^-2), which can be helped by pretending that angles have a dimension and treating the dimension of torques is Mass^1 Distance^2 Time^-2 Angle^-1. This does lead to some problems, though. For example, torque is no longer equal to the cross product of displacement and force. (Or is it? Both this use of the cross product and the use in defining curl seem to involve angles in the same way, so for a minute I considered changing the type of my cross product operator to introduce a factor of Angle^-1. I don't think it is helpful to do this, though, because I can't figure out how to stand on my head and imagine the use in the Lorentz force equation to involve implicit introduction and elimination of an angle, so it appears that this angular nature is not an inherent property of the cross product operator.) Another approach (not entirely an exclusive choice) is to distinguish through the vector nature of torque and the scalar nature of energy; now taking the norm is where your turns/degrees/radians ambiguity arises, and also it is not clear to me how this approach would work in fewer dimensions.

Should angles be modeled as a dimension for software engineering purposes? Should they be modeled in some other way? Should they be left implicit?

(To throw another wrench in the works, similar remarks apply to solid angles, although they may arise considerably less often for my target users. It also feels arbitrary to stop after 3 dimensions, and I assume--although I am not familiar with one--that there is some sort of generalization of solid angles to n-dimensional space.)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Quantities in Kawa

If it helps, I'm reminder of quantities in Per Bothner's Scheme Kawa, Summarizing it briefly, quantities are numbers with units, so instead of "2" it's "2 meters" or "2 centimeters" or "2 kilograms".

I was contemplating similar

I was contemplating similar questions just last month, along with thoughts on 'decibels' and other problematic units.

As I understand it, the real purpose of units is to encode more context and concepts into our numbers so they can be used more easily and safely. With respect to this purpose, it is a bad idea to lose information that a number represents an angle unless we choose to erase that information explicitly. An angle unit would also be effective at preventing simple mistakes.

Frink is a language with excellent support for units, and certainly the author has been contemplating the problem longer than me. In Frink, radians are not assigned a dimension, so we lose information that "this number represents an angle". But angle number conversions are still supported by such things as `4 degrees` or `0.02 radians` or `3 circles` or `12 arcmin`.

At this time, I favor modeling angle as a dimensioned unit.

Angles with units

I'm the author of Frink and I've struggled long and hard with the question of radians being their own dimension or non-dimensional, as noted above. (Actually, it wasn't that hard of a struggle because radians are dimensionless numbers.)

In short, Frink will allow radians (or degrees, or quadrants, or whatever) to be their own basic dimension or not, depending on how you define its standard data file. All of Frink's functions that deal with angles are radians-aware, and will behave appropriately if you define its standard data file so that radians are their own unit. I allow it, but not a single rational person ever uses it for long. (In more than ten years, I have used it exactly once to try and double-check "square degree" calculations on the sky. The standard Frink calculations were correct.)

The International System of Units (SI) had radians as its own unit up intil 1974, until when they changed it to just be another dimensionless number. Keep in mind that international standards bodies consider radians to be dimensionless. You *will* be the one to violate contracts if you treat them otherwise. However, read Frink's standard data file about how they broke the SI's definitions of basic circular / sinusoidal measures, though, especially the Hertz, with this change. Hint: search for "Alan's editorializing"

You can make Frink treat angles as their own basic dimension, but if you do this for long, you will realize that indeed radians are dimensionless (they're a length divided by a length) and if you try to make them their own units of measure, you will introduce artificiality into your computations that make no sense, and you'll have to add or remove those weird "radian" units manually.

For example, you learn in early math classes that (with x given in radians, which are dimensionless):

sin[x] = x - x^3/3! + x^5/5! - x^7/7! + ...

Just try to invent a dimensioning system where you introduce dimensions to x. It requires that x has the same dimensions as x^3. If you try to create a system for which this is correct, you will fail, unless x is dimensionless.

See the previous discussion.

A radian is a length divided by a length. It's dimensionless. If you can devise an alternate system that is correct in the Taylor series, and is self-consistent, you will beat hundreds of years of mathematicians.

Whatever/Whatever

If it is a ratio what happens if you introduce a ratio type? It might additionally be interpreted as whatever/whatever. Let whatever be whatever it needs to be.

international standards

international standards bodies consider radians to be dimensionless. You *will* be the one to violate contracts if you treat them otherwise.

I don't believe natural language contracts will be the issue. Adding a 'radians' dimension to my numbers is for the APIs and expressions within the language, and would serve a role for both documentation and safety.

you'll have to add or remove those weird "radian" units manually

My intuition: that's a good thing. Explicit is almost always better than implicit, assuming that it doesn't hinder abstraction.

For your Taylor series example, I'd check the dimension before stripping it and applying the computation. What does this gain me? Well, it means I'd raise an error (perhaps statically) if the argument to 'sin' does not represent an angle. There is no risk of me accidentally applying 'sin' to anything other than an angle. Also, it means that the numbers would be more meaningful and contextual when debugging or using APIs. I'd clearly know which numbers represent angles vs. scaling factors or other numbers.

Arbitrary, convertible choices

Most of the dimensions that are easy to talk about are managed using polynomials over the real numbers. We get away with this because if two people disagree about how to assign real numbers to physical lengths (or time, or mass), there's always some conversion factor that can be plugged in so that their polynomials correspond.

100 in^2

in = 2.54 cm

100 (2.54 cm)^2
= 645.16 cm^2

Overall, the use of a length unit implicitly says, "There exists an isomorphism by which these real numbers correspond to physical measurements. By the way, if you've heard of the standard length unit known as 'cm,' that's the one! Still, if you'd rather use a different isomorphism, that isomorphism is only a coefficient away from this one, so you can substitute every occurrence of this unit with a simple converted term to get the numbers you need."

When a measurement isomorphism doesn't convert to the real number line anymore, implicit assumptions in this notation break down. For instance, if the result I want to see is "2 pi radians," but someone gives me "0 radians," there's no conversion term I can substitute for their "radians" to get mine, because the isomorphisms we're using don't differ by a coefficient. If someone gives me "64 pi radians," I get to wonder if I'm allowed to add and remove revolutions or whether that would destroy information the author intended to convey.

Putting that all together, I'd say when we use an angle unit like "radians" in a revolution-oblivious way, it implicitly says this: "There exists an epimorphism by which these real numbers correspond to elements of the circle group. Namely, it's the epimorphism with a period of 2 pi. Still, if you'd rather use a different epimorphism, that epimorphism's period is only a coefficient away from this one, so you can substitute every occurrence of this unit with a simple converted term to get the numbers you need."

Again, any two reasonable conversions differ by a coefficient, so the multiplication-like notation "2 pi radians" is useful again. Notice that this coefficient is used to support an arbitrary choice of real-number-based notation, rather than an arbitrary choice of physical interpretation. There's no need for the latter; unlike the real numbers used for lengths and such, the circle group has only a single isomorphism to itself, and thus all its physical interpretations are equivalent.

A way to generalize this is to notice that a unit always involves the acknowledgment that some easily convertible choice (and sometimes an informal, physical choice) was made at a broad scope. Whatever this scope is, perhaps it should formally be under an existential quantifier to represent these choices, or perhaps (equivalently?) the choices should be nominal types which are local to that scope. (The scope will often need to span multiple modules, or else modules won't be able to share their dimensioned values.) The conversions should somehow be made available throughout that scope based on metadata about how the choices were made. For instance, perhaps every new unit definition for an existing dimension should provide a two-way conversion to an existing unit, while the language supports a "measure this dimensioned value using this unit" operator.

If that can be built, I think all the functionality of the unit "radians" can be accomplished as a combination of a few things: For revolution-preserving radians, just define a unit type for interpreting real numbers as revolution-preserving angles. For revolution-oblivious radians, define a type for the circle group, define a unit type for interpreting circle group elements as revolution-oblivious angles, and define a conversion function "radians" that takes a real number, converts it to an element of the circle group according to a period of 2 pi, and applies the revolution-oblivious angle unit type to that value.

I arrived at this approach the last time a thread like this came up, but I don't recall if I posted it.

This is true of units, which

This is true of units, which can therefore be freely inter-converted by the system dynamically. But as far as I can tell, it isn't useful in any particular sense for dimensions, whose existence is static.

This is because 1 Farad < 37 miles is a type error if you use the SI basis for dimensions (Time^4 Current^2 Length^-2 Mass^-1 is incomparable with Length^1), but makes perfect sense if you use the cgs basis (Length^1 is the dimension of both expressions).

Unrelatedly, I'm not sure I follow the distinction you are drawing between revolution-preserving and revolution-oblivious radians.

cgs vs Gaussian

To clarify: The cgs system doesn't actually say anything about the electromagnetic units. It only defines length, mass and time (centimeters, grams, and seconds, thus the abbreviation). To extend the measurements to electromagnetic charge (or elecromagnetic current; one person's static charge is another moving person's current) you have to introduce new units, and there are several competing systems, for example, the Gaussian system and the Lorentz-Heaviside.

The Farad is a measure of capacitance in the SI system, but in the (broken, non-orthogonal) Gaussian system, which I assume that the previous comment referred to, capacitance has the same dimensions as length. Which should be a red flag to anyone who wants to use it. Just don't.

Please study the Buckingham pi theorem to see what it takes to be an orthogonal system of units. But don't assume that following this makes all of your units orthogonal.

Indeed

Solid point, and I appreciate the correction.

The problem I was trying to point out, namely that these different choices are not freely convertible, is something I wanted to call attention to because if only ever used the SI system one might not be aware that it exists.

Dimensions vs Units

As you touched upon, there is a difference between dimensions and units. Your problem with angles has nothing to do with the fact they're dimensionless; it's to do with their units. Quantities with a dimension can suffer exactly the same problem of inconsistent units, for example: http://en.wikipedia.org/wiki/Mars_Climate_Orbiter

I'd recommend keeping values, units and dimensions separate, then defining valid combinations. Perhaps something like the following:

-- We represent quantities as a value, a unit and a dimension
data Qty v u d = Qty v u d

-- A representation is only a valid Quantity if its value is a Num
-- and its unit and dimension match
class (Num v, UnitOf u d) => Quantity (Qty v u d) where

-- UnitOf matches units with dimensions
class (Unit u, Dimension d) => UnitOf u d where

-- Particular dimensions
data Length = Length
data Mass = Mass
data Charge = Charge
data Dimensionless = Dimensionless
data Inverse a = Inverse a
data DimProd a b = DimProd a b

class Dimension a where
instance Dimension Length
instance Dimension Mass
instance Dimension Charge
instance Dimension Dimensionless
instance (Dimension a) => Dimension (Inverse a)
instance (Dimension a, Dimension b) => Dimension (DimProd a b)

-- Associate a dimension with out units
instance (LengthUnit u) => UnitOf u Length
instance (MassUnit u) => UnitOf u Mass
instance (ChargeUnit u) => UnitOf u Charge
instance (DimensionlessUnit u) => UnitOf u Dimensionless

-- Particular units
data Metre = Metre
data Foot = Foot
data Parsec = Parsec

class LengthUnit a where
instance LengthUnit Metre
instance LengthUnit Foot
instance LengthUnit Parsec

data Gram = Gram
data Kilogram = Kilogram

class MassUnit a where
instance MassUnit Gram
instance MassUnit Kilogram

data Radian = Radian
data Degree = Degree
data Raw = Raw

class DimensionlessUnit a where
instance DimensionlessUnit Radian
instance DimensionlessUnit Degree
instance DimensionlessUnit Raw

data Coulomb = Coulomb
data ElectronCharge = ElectronCharge

class ChargeUnit a where
instance ChargeUnit Coulomb
instance ChargeUnit ElectronCharge

-- We can convert between units of the same dimension
class (Dimension d, UnitOf u1 d, UnitOf u2 d) => Convertible (d, (u1, u2)) where
  unitConv :: Qty v u1 d -> u2 -> Qty v u2 d

-- Particular conversions
instance Convertible (MassUnit, (Gram, Kilogram)) where
  unitConv (Qty v Gram Mass) Kilogram = Qty (1000 * v) Kilogram Mass

instance Convertible (DimensionlessUnit (Radian, Degree)) where
  unitConv (Qty v Radian Dimensionless) Degree = Qty (180 * v / pi) Degree Dimensionless

This won't quite work as-is, but it's along the right track. It seems pretty verbose, but a lot of it can probably be hidden with smart constructors and arithmetic functions, which I'll leave as an exercise for you ;)

I've chosen Haskell here, but it would probably be much easier to express these relations in a logic language like Prolog or Mercury, or perhaps a rewrite language like Maude or Pure.

Statics of units

I like where you are going with this, but I have two concerns.

First is that this doesn't actually eliminate the need to choose a basis for the dimensions, you have just made a concrete choice. (To see why it matters, note that 1 Farad < 37 miles is a type error if you use the SI basis for dimensions (Time^4 Current^2 Length^-2 Mass^-1 is incomparable with Length^1), but makes perfect sense if you use the cgs basis (Length^1 is the dimension of both expressions).

Second, I think I want to make units have no static existence. I have variants of my toy library where they exist statically and variants where they do not, and I find the latter more useful. The primary use to maintaining them seems to be that you can specify fixed-point types in whichever units you choose, which is attractive especially for resource-constrained code. The primary drawback is that interfaces become cluttered with barely-useful information about the units which obscures the important stuff (the dimensions).

Tracking dimensionless units is nice in that you don't lose the factors of 2 pi so easily. But it still allows comparing things which have incomparable types in the system where you track angles as a dimension where the physical comparison makes no sense. It might be the way to go, I am just not sure yet.

Units flexibility and Frink

Again, the cgs system doesn't say anything about electromagnetics. See my comments above.

My language Frink allows you to use any dimensional system that you choose, dynamically, at runtime. You can even use Planck/"natural" units where common exponents are rational numbers. You can use more or fewer basic dimensions. It *will* help you identify places where your unit system doesn't make sense (e.g. angles vs. dimensionless numbers). I suggest defining your own units file (Frink's standard data file is here) with the -u [filename] option on the command-line to use your own. Try it out a while. It will require you to be rigorous.

Fundamentally, units are

Fundamentally, units are about symmetries. I've wrote about this before on LTU and got an reply pointing me to an excellent paper that explores this idea. In their system you could also give correct units to functions like sin and cos. In physics where rotational symmetry holds you will never see an expression like sin(x) + cos(x) because this is dimensionally inconsistent for a sufficiently general notion of dimensions. This is analogous to why you'd never see an expression like y + y*y in physics where scale invariance holds.

Excellent reference. I had

Excellent reference. I had forgotten about last year's unit discussion.

I'm interested in understanding units (conflating dimensions) and their relationship to symmetry better. I believe units are a very good fit for augmenting structurally typed languages, like those I'm developing.

Seconded

I agree with dmbarbour, this paper is great stuff.

To float the link out a level, it's Abstraction and Invariance for Algebraically Indexed Types by Robert Atkey, Patricia Johann, and Andrew Kennedy.

Symmetry via types

I wrote a followup paper on using group-indexed types to enforce the symmetries that you need in classical mechanics to apply Noether's theorem to get conservation laws: From parametricity to Conservation laws, via Noether's theorem. This re-uses the core geometry example from the algebraically-indexed types paper, places it in a more general semantic setting (interpreting kinds as reflexive graphs), and provides a little DSL for writing smooth functions. I also give types to the sin and cosine functions that captures the fact that they are periodic, and so states at least one of the differences between them and most other functions that operate on the reals.

Never?

I'm just curious about these statements. You say that "you will never see an expression like sin(x) + cos(x) because this is dimensionally inconsistent for a sufficiently general notion of dimensions."

How much "like" is "like"? For example, in the most basic matrix equations for rotation about the origin, you see the equations like:

y' = x sin[angle] + y cos[angle]

If x = y, this basically becomes sin(x) + cos(x).

From writing dimension-independent graphics libraries, I'm not sure that we can never say never. Can you clarify this "never"?

In a unit system that

In a unit system that captures rotational invariance, x and y would have different units. x would have unit "meters in x direction" and y would have unit "meters in y direction". By the way in such a unit system x+y would also be dimensionally inconsistent, as it should be, since x+y will give different results depending on how you happened to choose you coordinate axes directions and therefore it has no physical meaning.

Wikipedia has a short section about this: http://en.wikipedia.org/wiki/Dimensional_analysis#Siano.27s_extension:_orientational_analysis

There is broad agreement

There is broad agreement (for decent reasons) that angles are dimensionless, and there is a long history of mostly treating them that way in the mathematics, physics, and engineering literature. Yet pragmatically, serious errors can arise when revolutions are mistaken for radians or angular velocities are confused with frequencies.

More context for numbers is always a good thing IMO. Back when I was studying electrical engineering, we always cited units for all quantities. Frequency was cycles/second, and angles were almost universally radians.

radians are the natural way

radians are the natural way to think about angles because they are pure numbers. For ease we say that something is in radians to signal that the purpose of the pure number is to measure angles.

Devil's advocate

I'll play the devil's advocate for a minute.

Aren't turns the natural way to think about angles? Especially in a computing context. Exact rational arithmetic applied to angles measured in turns is extremely useful for a lot of purposes (geometry, gear ratios, and so forth). Exact rational arithmetic applied to angles measured in radians is, as far as I can tell, completely useless.

Sure, the definitions of sine and cosine get a bit uglier, and the relationship between trigonometric functions and imaginary powers of e gets a bit uglier because the 2 pi's migrate there, but maybe it's better to have them clutter up a few core definitions than to have them cluttering up every angle we measure.

I don't think I actually hold this position. But I do think it suggests that, just as different units are useful in different situations for measuring anything else, there are situations where angles are more usefully expressed in various ways. It would be helpful if our systems could help us use the most convenient ways but still prevent us from crossing them up.

I also think this (the claim of turns to "naturalness") points to why the frequency/angular velocity mistake is so easy to make.

Already advocated

In many cases sin and cos

In many cases sin and cos are unnecessary. When you are dealing with triangles, you often compute an angle from some lengths, then you add or subtract some angles, and then you take the sin or cos to turn it back into lengths. In these cases you can obtain an exact answer using the sin/cos addition laws. But it is much better to eliminate the step to and from angles. Angles are transcendental (even if you express them as turns) and therefore hard to deal with. Instead of dealing with an angle a, you can always deal with sin(a) instead which is just a ratio between two lengths.

Of course when you are dealing with genuine rotation rather than with geometry of lines, this doesn't work. If you want to rotate something at a fixed rate, there is no way of doing that without transcendental numbers since that is inherent in the problem. In that case I do find complex exponentials to be much easier to deal with than sin & cos however.

Optimization?

I'm not sure I understand the objection here.

Certainly there are ways of rearranging expressions to make it unnecessary to compute sines and cosines explicitly. But I'm not sure that's all that important when trying to work out the type system.

I think I disagree with "Instead of dealing with an angle a, you can always deal with sin(a) instead which is just a ratio between two lengths." I don't think that this is the case because one is periodic while the other is not. I suppose it depends on your definition of "angle". In some situations we want to treat pi and 3 pi as equal, in others we do not, but we generally use the same word in both situations. (Similar problems arise with other things too, for example absolute and relative temperatures or times.)

Of course, we can hardly have the position that pi and 3 pi are equal and that angles shouldn't be represented in the type system.

Frequency vs. the SI

Frequency should be in cycles per time. You are correct.

So, quick question: How many radians per second is one Hz? Think about that before reading further.

The answer is that you're wrong at one time or another because your international standards bodies are wrong and stupid. Really, though, you're not actually wrong; your international standards bodies are incompetent and wrong. But they make you wrong by law.

If you want to see how badly international standards bodies have broken that definition, you should look into the definition of the Hz.

I have extensive documentation about the ridiculous definition of the Hz under the current SI. Please read Frink's standard data file and search for the term "stupid definition". (It is one of many stupid definitions.)

In short: The SI's current definition of the Hz says that 1 Hz = 1 radian/s. It is stupid. It is wrong. You would have failed all of your physics or electrical engineering classes ever if you used this definition, but it's what incompetent international standards bodies have defined.

Why is this wrong? Droplets

Why is this wrong? Droplets of water are falling from a tap at a rate of 1 droplet per second. Using your preferred definition of Hz that would be at a rate of ~0.32 Hz. This is weird. Not all things that are happening repeatedly in time are about rotation, so to me it does not make sense to bake radians into Hz. Even with things that do involve sines and cosines things get confusing if you use Hz = 2*pi rad/second (edit: was 1 rad/second). Consider a sound wave of 100 Hz. With your definition that wave would be oscillating approximately 314 times per second. Personally I would be happy without Hz at all, and just using "per second" and "radians per second" directly.

I think you misunderstood

I think you misunderstood the comment you were replying to. He called Hz=radians/second stupid and wrong, which is what you just argued as well.

Actually I tried to argue

Actually I tried to argue the opposite, but I made a mistake that made it say the opposite opposite (now edited)...anyway I'm not really sure exactly who is saying what anymore...

I'm not sure that radians

I'm not sure that radians and frequency have any canonical relationship that ought to trump all others. What constitutes a "full cycle" seems context dependent. Spin-1/2 particles require two full "rotations" to return to the initial configuration, for instance.

So I think this is agreeing with what Jules' revised note says, ie. I disagree with both Frink's and the SI's definition of Hz via radians. Hz seems like it should be defined by some temporal symmetry of the system in question, not some ill-fitting universal geometry that may not have any meaning in said system.

My intuition tells me that there is an important theorem

My intuition tells me that there is an important theorem of dimensional analysis that shouldn't be overlooked by language implementors willing to integrate unit systems in their language design:

(linking directly to the simple example demonstrating the general procedure that would yield - if success - or not - if error - the homogeneous dimensional equation)

http://en.wikipedia.org/wiki/Buckingham_%CF%80_theorem#Speed

Might be beneficial some place in a type checker / AST builder?

Note this reserve, though (quoting WP):

[...] The fact that there is only a single value of C and that it is equal to unity is a level of detail not provided by the technique of dimensional analysis.

Buckingham Pi theorem

The Buckingham Pi theorem was covered in Andrew Kennedy's original paper on the semantics of units-of-measure type systems Relational Parametricity and Units of Measure. Within the context of a programming language, it manifests as a type isomorphism between a "richly unit-ed" function type and a unit-less function type with fewer parameters.