bearophile wrote:
Scala has a powerful type system that allows to implement such things in a good 
enough way:

http://www.michaelnygard.com/blog/2009/05/units_of_measure_in_scala.html

So does D:
===============================================
// by Oskar Linde Aug 2006
// This is just a quick hack to test
// IFTI operators opMul and opDel

import std.stdio;
import std.math;
import std.string;

version = unicode;

struct SiQuantity(T,int e1, int e2, int e3, int e4, int e5, int e6, int e7) {
        T value = 0;
        alias T ValueType;
        const exp1 = e1;
        const exp2 = e2;
        const exp3 = e3;
        const exp4 = e4;
        const exp5 = e5;
        const exp6 = e6;
        const exp7 = e7;

        static assert(SiQuantity.sizeof == ValueType.sizeof);

        template AddDimensions(int mul, U) {
                static assert(is(U.ValueType == ValueType) ||
                              is(U == ValueType),
                              "incompatible value types");
                static if (is(U == ValueType))
                        alias SiQuantity AddDimensions;
                else
                        alias SiQuantity!(T,exp1+mul*U.exp1,exp2+mul*U.exp2,
                                          exp3+mul*U.exp3,exp4+mul*U.exp4,
                                          exp5+mul*U.exp5,exp6+mul*U.exp6,
                                          exp7+U.exp7) AddDimensions;
        }

        SiQuantity opAddAssign(SiQuantity rhs) {
                value += rhs.value;
                return this;
        }

        SiQuantity opSubAssign(SiQuantity rhs) {
                value -= rhs.value;
                return this;
        }

    const
    {

        SiQuantity opAdd(SiQuantity rhs) {
                SiQuantity ret;
                ret.value = value + rhs.value;
                return ret;
        }

        SiQuantity opSub(SiQuantity rhs) {
                SiQuantity ret;
                ret.value = value - rhs.value;
                return ret;
        }

        SiQuantity opNeg() {
                SiQuantity ret;
                ret.value = -value;
                return ret;
        }

        SiQuantity opPos() {
                typeof(return) ret;
                ret.value = value;
                return ret;
        }

        int opCmp(SiQuantity rhs) {
                if (value > rhs.value)
                        return 1;
                if (value < rhs.value)
                        return -1;
                return 0; // BUG: NaN
        }

        AddDimensions!(+1,Rhs) opMul(Rhs)(Rhs rhs) {
                AddDimensions!(+1,Rhs) ret;
                static if (is(Rhs : T))
                        ret.value = value * rhs;
                else
                        ret.value = value * rhs.value;
                return ret;
        }

        AddDimensions!(-1,Rhs) opDiv(Rhs)(Rhs rhs) {
                AddDimensions!(-1,Rhs) ret;
                static if (is(Rhs : T))
                        ret.value = value / rhs;
                else
                        ret.value = value / rhs.value;
                return ret;
        }

        SiQuantity opMul_r(T lhs) {
                SiQuantity ret;
                ret.value = lhs * value;
                return ret;
        }

        SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) opDiv_r(T lhs) {
                SiQuantity!(T,-e1,-e2,-e3,-e4,-e5,-e6,-e7) ret;
                ret.value = lhs / value;
                return ret;
        }

        string toString() {
                string prefix = "";
                T multiplier = 1;
                T value = this.value;
                string unit;
                
                static if (is(typeof(UnitName!(SiQuantity))))
                        unit = UnitName!(SiQuantity);
                else {
                        value *= pow(cast(real)1e3,cast(uint)e2); // convert kg 
-> g
                        // Take mass (e2) first to handle kg->g prefix issue 
                        if (e2 != 0) unit ~= format("·g^%s",e2);
                        if (e1 != 0) unit ~= format("·m^%s",e1);
                        if (e3 != 0) unit ~= format("·s^%s",e3);
                        if (e4 != 0) unit ~= format("·A^%s",e4);
                        if (e5 != 0) unit ~= format("·K^%s",e5);
                        if (e6 != 0) unit ~= format("·mol^%s",e6);
                        if (e7 != 0) unit ~= format("·cd^%s",e7);
                        if (unit)
                                unit = unit[2..$].split("^1").join("");
                }

                if (value >= 1e24) { prefix = "Y"; multiplier = 1e24; }
                else if (value >= 1e21) { prefix = "Z"; multiplier = 1e21; }
                else if (value >= 1e18) { prefix = "E"; multiplier = 1e18; }
                else if (value >= 1e15) { prefix = "P"; multiplier = 1e15; }
                else if (value >= 1e12) { prefix = "T"; multiplier = 1e12; }
                else if (value >= 1e9) { prefix = "G"; multiplier = 1e9; }
                else if (value >= 1e6) { prefix = "M"; multiplier = 1e6; }
                else if (value >= 1e3) { prefix = "k"; multiplier = 1e3; }
                else if (value >= 1) { }
                else if (value >= 1e-3) { prefix = "m"; multiplier = 1e-3; }
                else if (value >= 1e-6) {
                        version(unicode)
                                prefix = "µ";
                        else
                                prefix = "u";
                        multiplier = 1e-6; }
                else if (value >= 1e-9) { prefix = "n"; multiplier = 1e-9; }
                else if (value >= 1e-12) { prefix = "p"; multiplier = 1e-12; }
                else if (value >= 1e-15) { prefix = "f"; multiplier = 1e-15; }
                else if (value >= 1e-18) { prefix = "a"; multiplier = 1e-18; }
                else if (value >= 1e-21) { prefix = "z"; multiplier = 1e-21; }
                else if (value >= 1e-24) { prefix = "y"; multiplier = 1e-24; }

                return format("%.3s %s%s",value/multiplier, prefix, unit);
        }       
    }
}

//length                        meter           m
//mass                          kilogram        kg
//time                          second          s
//electric current              ampere          A
//thermodynamic temperature     kelvin          K
//amount of substance           mole            mol
//luminous intensity            candela         cd

// Si base quantities
alias SiQuantity!(real,1,0,0,0,0,0,0) Length;
alias SiQuantity!(real,0,1,0,0,0,0,0) Mass;
alias SiQuantity!(real,0,0,1,0,0,0,0) Time;
alias SiQuantity!(real,0,0,0,1,0,0,0) Current;
alias SiQuantity!(real,0,0,0,0,1,0,0) Temperature;
alias SiQuantity!(real,0,0,0,0,0,1,0) AmountOfSubstance;
alias SiQuantity!(real,0,0,0,0,0,0,1) Intensity;

alias SiQuantity!(real,0,0,0,0,0,0,0) UnitLess;

// Derived quantities
alias typeof(Length*Length)             Area;
alias typeof(Length*Area)               Volume;
alias typeof(Mass/Volume)               Density;
alias typeof(Length*Mass/Time/Time)     Force;
alias typeof(1/Time)                    Frequency;
alias typeof(Force/Area)                Pressure;
alias typeof(Force*Length)              Energy;
alias typeof(Energy/Time)               Power;
alias typeof(Time*Current)              Charge;
alias typeof(Power/Current)             Voltage;
alias typeof(Charge/Voltage)            Capacitance;
alias typeof(Voltage/Current)           Resistance;
alias typeof(1/Resistance)              Conductance;
alias typeof(Voltage*Time)              MagneticFlux;
alias typeof(MagneticFlux/Area)         MagneticFluxDensity;
alias typeof(MagneticFlux/Current)      Inductance;
alias typeof(Intensity*UnitLess)        LuminousFlux;
alias typeof(LuminousFlux/Area)         Illuminance;

// SI fundamental units
const Length            meter           = {1};
const Mass              kilogram        = {1};
const Time              second          = {1};
const Current           ampere          = {1};
const Temperature       kelvin          = {1};
const AmountOfSubstance mole            = {1};
const Intensity         candela         = {1};

// Derived units
const Frequency         hertz           = {1};
const Force             newton          = {1};
const Pressure          pascal          = {1};
const Energy            joule           = {1};
const Power             watt            = {1};
const Charge            coulomb         = {1};
const Voltage           volt            = {1};
const Capacitance       farad           = {1};
const Resistance        ohm             = {1};
const Conductance       siemens         = {1};
const MagneticFlux      weber           = {1};
const MagneticFluxDensity tesla         = {1};
const Inductance        henry           = {1};
const LuminousFlux      lumen           = {1};
const Illuminance       lux             = {1};

template UnitName(U:Frequency)  { const UnitName = "Hz"; }
template UnitName(U:Force)      { const UnitName = "N"; }
template UnitName(U:Pressure)   { const UnitName = "Pa"; }
template UnitName(U:Energy)     { const UnitName = "J"; }
template UnitName(U:Power)      { const UnitName = "W"; }
template UnitName(U:Charge)     { const UnitName = "C"; }
template UnitName(U:Voltage)    { const UnitName = "V"; }
template UnitName(U:Capacitance){ const UnitName = "F"; }
version(unicode) {
        template UnitName(U:Resistance) { const UnitName = "?"; }
} else {
        template UnitName(U:Resistance) { const UnitNAme = "ohm"; }
}       
template UnitName(U:Conductance){ const UnitName = "S"; }
template UnitName(U:MagneticFlux){ const UnitName = "Wb"; }
template UnitName(U:MagneticFluxDensity) { const UnitName = "T"; }
template UnitName(U:Inductance) { const UnitName = "H"; }

void main() {
        Area a = 25 * meter * meter;
        Length l = 10 * 1e3 * meter;
        Volume vol = a * l;
        Mass m = 100 * kilogram;
        assert(!is(typeof(vol / m) == Density));
        //Density density = vol / m; // dimension error -> syntax error
        Density density = m / vol;
        writefln("The volume is %s",vol.toString);
        writefln("The mass is %s",m.toString);
        writefln("The density is %s",density.toString);

        writef("\nElectrical example:\n\n");
        Voltage v = 5 * volt;
        Resistance r =  1 * 1e3 * ohm;
        Current i = v/r;
        Time ti = 1 * second;
        Power w = v*v/r;
        Energy e = w * ti;

        // One wishes the .toString was unnecessary...
        writefln("A current of ",i.toString);
        writefln("through a voltage of ",v.toString);
        writefln("requires a resistance of ",r.toString);
        writefln("and produces ",w.toString," of heat.");
        writefln("Total energy used in ",ti.toString," is ",e.toString);

        writef("\nCapacitor time curve:\n\n");
        
        Capacitance C = 0.47 * 1e-6 * farad;    // Capacitance
        Voltage V0 = 5 * volt;                  // Starting voltage
        Resistance R = 4.7 * 1e3 * ohm;         // Resistance

        for (Time t; t < 51 * 1e-3 * second; t += 1e-3 * second) {
                Voltage Vt = V0 * exp((-t / (R*C)).value);

                writefln("at %5s the voltage is %s",t.toString,Vt.toString);
        }
}

Reply via email to