Units of Measure in F#

Denis Koroskin 2korden at gmail.com
Tue Oct 7 12:26:24 PDT 2008


On Tue, 07 Oct 2008 22:39:10 +0400, BCS <ao at pathlink.com> wrote:

> Reply to bearophile,
>
>> BCS:
>>
>>> svn.dsource.org  seems to be having problmes or I'd post a system I
>>> just
>>> put together.
>>> total code, WS comments: ~ 400 LOC
>>> supports 44 different units,
>>> support +,-,* and / as well as pow and root
>> A lot of work. Does is use a (syntax) strategy similar to this?
>> http://www.boost.org/doc/libs/1_36_0/doc/html/boost_units.html
>>  Now D just needs a pow infix operator (** ?) and that's done :-)
>>  Bye,
>> bearophile
>
> SVN is working again (or I'm somewhere it works from)
>
> http://www.dsource.org/projects/scrapple/browser/trunk/units/
>
> take a look at si.d first as it's the most useful intro (look way down  
> at the bottom)
>
>

Well, I have done it completely different. Here is my (simplified) class  
hierarchy from memory:

// Unit is 's', 'kg', 'n' etc, i.e. they are basic orthogonal units
class Unit(string name)
{
     enum AsString = name;
}

// A Powered Unit :) PUnit is s^2, kg^(-3.14) etc.
// It is
class PUnit(Unit, float power)
{
     alias UnitType Unit;
     enum Power = power;
}

// Quantity consists of a unique set of PUnits and a value. It also
// defines a set of operations like opMul, opDiv, opAdd and opSub
// Example: 5 m/s^2
class Quantity(U...)
{
     alias Units U;

     private float value;

     // here is how my opMul looks like:
     Multiply!(Units, OtherUnits) opMul(OtherUnits)(OtherUnits other)
     {
         Multiply!(Units, OtherUnits) result = void;
         result.value = value * other.value;
         return result;
     }
}

here is how I merge Units for multiplication:

template GetUnitPower(Unit, Units...)
{
     static if (Units.length == 0) {
         enum GetUnitPower = 0;
     } else static if (is (Units[0].UnitType == Unit)) {
         enum GetUnitPower = Units[0].Power;
     } else {
         enum GetUnitPower = GetUnitPower!(Unit, Units[1..$]);
     }
}

template AddPowers(Unit, Units...)
{
     // GetUnitPower returns 0 if there is no such Unit in Units
     // put '-' for Divide! here
     enum Power = Unit.Power + GetUnitPower!(Unit.UnitType, Units);

     // get all the Units without Unit
     alias Without!(Unit.UnitType, Units) Rest;

     // Add the Unit with a new Power to the list of rest units
     alias Tuple!(PUnit!(Unit.UnitType, Power), Rest) Result;
}

As a result you can have any arbitrary amount of orthogonal Units. Add  
them with a single line:

mixin(defineUnit("Time", "s"));
mixin(defineUnit("Mass", "kg"));
mixin(defineUnit("Distance", "m"));
mixin(defineUnit("Speed", "m/s"));

Distance d = 6 * m;
Time t = 3 * s;
Speed s = d / t;



More information about the Digitalmars-d mailing list