Since we don't want space probes written in Factor to crash because of calculations on mismatching units, Doug Coleman wrote a units library:

10 feet 30 inches d+ 56 s d/

It can add, multiply and divide dimensioned quantities while converting on the fly and making sure all the units make sense (

`10 miles 30 teaspoons d+`

is a runtime error).

There was one problem with this library though, and it was that converting a dimensioned quantity back to a scalar required some boilerplate; for example, we have the following code:

: inches ( n -- dimensioned ) 2.54 * cm ;

: inches> ( dimensioned -- n )

dup 0 { m } { } =dimensions?*

dimensioned-value 100 * 2.54 / ;

The

`inches`

word converts a number of inches to centimeters;

`cm`

in turn converts it to meters, which is the canonical representation of distances, then creates a dimensioned quantity. However the

`inches>`

word is ugly, and it is all boilerplate since the defintion of

`inches`

has all the required information.

Let's take a look at

`cm`

:

: cm centi m ;

And its auxilliary words:

: centi 100 / ;

: m ( n -- dimensioned ) { m } { } <dimensioned> ;

As you can see,

`<dimensioned>`

is the canonical constructor.

Now, enter Daniel Ehrenberg's inverse library. While Dan originally intended to use it for

pattern matching, this library is a perfect fit for auto-generating words for converting dimensioned quantities back to scalars.

Indeed, out of the box, it cannot invert

`<dimensioned>`

, because it calls

`natural-sort`

which is not an invertible operation. However, we can explicitly define an inverse for

`<dimensioned>`

(not an inverse in the mathematical sense, since the original function is not one-to-one; strictly speaking, this is a

*section*, that is, a function which satisfies only one of the two conditions of an inverse).

And now, everything works; here,

`undo`

is Dan's inversion combinator:

100 cm [ inches ] undo .

5000/127

So

`100 cm`

constructs a dimensioned object, after converting the dimension to canonical form (meters in the case of distance):

100 cm .

T{ dimensioned f 1 { m } { } }

And

`[ inches ] undo`

takes a dimensioned quantity, and works backwards in order to obtain a number, which when passed to

`inches`

, would give the original quantity:

[ inches ] undo .

5000/127

Lets take a look at the code generated by

`[ inches ] undo`

:

[ inches ] [undo] .

[ >dimensioned< { } =/fail { m } =/fail 100 * 127/50 / ]

It takes the dimensioned quantity apart, makes sure it is a distance (and not a time, etc), then multiplies the quantity itself by a conversion factor.

So we can convert a scalar represented by unit to any other unit in this way; we can also do stuff like:

10 miles 30 km d+ [ yards ] undo .

19205600/381

What is shorter, a quarter of a mile or 400 meters?

1/4 miles 400 m d< .

f

We also support compound units, such as miles per second, miles per second squared, etc. How many teaspoons in a liter plus a tablespoon?

1 L 1 tablespoons d+ [ teaspoons ] undo .

77937/379

For volume units, the canonical representation is meters cubed:

15 teaspoons .

T{ dimensioned f 379/5120000 { m m m } { } }

Here is a unit with a denominator:

20 knots .

T{ dimensioned f 463/45 { m } { s } }

The inverse library automatically guards against invalid conversions:

30 km 10 s d/ [ teaspoons ] undo .

Unification failed

This is seriously cool stuff. The

implementation of units is very simple and elegant, too; only half of the code has to be written, because we can use

`inverse`

!

The

`inverse`

library depends on two things; the simple mapping between syntax and semantics (the composition of two functions is their concatenation, therefore the inverse of a concatenation of two functions is a concatenation of their inverses!) and it also depends on quotations being sequences rather than opaque code blocks. Two qualities which are unique to Joy dialects such as Factor.