Units of Measure in F#

Walter Bright newshound1 at digitalmars.com
Wed Oct 8 22:03:39 PDT 2008


This has been done in D already:
----------------------------------
// 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);
	}
}



More information about the Digitalmars-d mailing list