digitalmars.D - Units of Measure in F#
- bearophile <bearophileHUGS lycos.com> Oct 06 2008
- "Nick Sabalausky" <a a.a> Oct 06 2008
- BCS <ao pathlink.com> Oct 06 2008
- "Denis Koroskin" <2korden gmail.com> Oct 06 2008
- BCS <ao pathlink.com> Oct 06 2008
- bearophile <bearophileHUGS lycos.com> Oct 07 2008
- BCS <ao pathlink.com> Oct 07 2008
- bearophile <bearophileHUGS lycos.com> Oct 07 2008
- BCS <ao pathlink.com> Oct 07 2008
- "Denis Koroskin" <2korden gmail.com> Oct 07 2008
- ore-sama <spam here.lot> Oct 07 2008
- Walter Bright <newshound1 digitalmars.com> Oct 08 2008
- BCS <ao pathlink.com> Oct 09 2008
- "Denis Koroskin" <2korden gmail.com> Oct 09 2008
- BCS <ao pathlink.com> Oct 09 2008
I have found this interesting old thread, I don't know how much those things are true today too: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=36939 Beside allowing algebraic data types, that are quite useful, a functional-like type system allows to implement Units of Measure in a nice way: http://blogs.msdn.com/andrewkennedy/archive/2008/08/20/units-of-measure-in-f-part-one-introducing-units.aspx For people that don't remember what algebraic data types are: http://en.wikipedia.org/wiki/Algebraic_data_type This power also allows to use pattern matching, absent in Python, present in Haskell, Ocaml, Scala, etc. Bye, bearophile
Oct 06 2008
"bearophile" <bearophileHUGS lycos.com> wrote in message news:gcdt5p$10t0$1 digitalmars.com...I have found this interesting old thread, I don't know how much those things are true today too: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=36939 Beside allowing algebraic data types, that are quite useful, a functional-like type system allows to implement Units of Measure in a nice way: http://blogs.msdn.com/andrewkennedy/archive/2008/08/20/units-of-measure-in-f-part-one-introducing-units.aspx For people that don't remember what algebraic data types are: http://en.wikipedia.org/wiki/Algebraic_data_type This power also allows to use pattern matching, absent in Python, present in Haskell, Ocaml, Scala, etc. Bye, bearophile
That's awesome. I want it!
Oct 06 2008
Reply to bearophile,I have found this interesting old thread, I don't know how much those things are true today too: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmar s.D&article_id=36939 Beside allowing algebraic data types, that are quite useful, a functional-like type system allows to implement Units of Measure in a nice way: http://blogs.msdn.com/andrewkennedy/archive/2008/08/20/units-of-measur e-in-f-part-one-introducing-units.aspx For people that don't remember what algebraic data types are: http://en.wikipedia.org/wiki/Algebraic_data_type This power also allows to use pattern matching, absent in Python, present in Haskell, Ocaml, Scala, etc. Bye, bearophile
I think it is fully doable right now. I've considered doing it off and on for some time. I even figured out how to make it do rational powers for the dimensions. The only thing I would want is to be able to avoid needing to wrap everything by way of operator overloads on typedefs typedef real Unit(..stuff..) { Unit!(stuff) opAdd(T)(T t) {...} } and some way to re-type functions alias sqrt Unit!(stuff/2) sqrt(Unit!(stuff)); // if sqrt called with Unit!(stuff), return type is Unit!(stuff/2)
Oct 06 2008
On Tue, 07 Oct 2008 00:41:29 +0400, bearophile <bearophileHUGS lycos.com> wrote:I have found this interesting old thread, I don't know how much those things are true today too: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=36939 Beside allowing algebraic data types, that are quite useful, a functional-like type system allows to implement Units of Measure in a nice way: http://blogs.msdn.com/andrewkennedy/archive/2008/08/20/units-of-measure-in-f-part-one-introducing-units.aspx For people that don't remember what algebraic data types are: http://en.wikipedia.org/wiki/Algebraic_data_type This power also allows to use pattern matching, absent in Python, present in Haskell, Ocaml, Scala, etc. Bye, bearophile
Arghh! I've almost done the trick with D templates and then I got this: Assertion failure: 'i < parameters->dim' on line 784 in file 'template.c' DMD is not ready for my funky templates yet! :) I'll post my results (and a bug report) soon.
Oct 06 2008
Reply to Denis,On Tue, 07 Oct 2008 00:41:29 +0400, bearophile <bearophileHUGS lycos.com> wrote:I have found this interesting old thread, I don't know how much those things are true today too: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma rs.D&article_id=36939 Beside allowing algebraic data types, that are quite useful, a functional-like type system allows to implement Units of Measure in a nice way: http://blogs.msdn.com/andrewkennedy/archive/2008/08/20/units-of-measu re-in-f-part-one-introducing-units.aspx For people that don't remember what algebraic data types are: http://en.wikipedia.org/wiki/Algebraic_data_type This power also allows to use pattern matching, absent in Python, present in Haskell, Ocaml, Scala, etc. Bye, bearophile
this: Assertion failure: 'i < parameters->dim' on line 784 in file 'template.c' DMD is not ready for my funky templates yet! :) I'll post my results (and a bug report) soon.
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
Oct 06 2008
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
Oct 07 2008
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
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)
Oct 07 2008
BCS:take a look at si.d first as it's the most useful intro (look way down at the bottom)
I think there's a need of some syntactic sugar :-) Bye, bearophile
Oct 07 2008
Reply to bearophile,BCS:take a look at si.d first as it's the most useful intro (look way down at the bottom)
Bye, bearophile
I put up some better examples. http://www.dsource.org/projects/scrapple/browser/trunk/units/si.d
Oct 07 2008
On Tue, 07 Oct 2008 22:39:10 +0400, BCS <ao 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
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;
Oct 07 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);
}
}
Oct 08 2008
Reply to Walter,This has been done in D already:
It has some interesting features :) (I might have to steal a few for my version)
Oct 09 2008
On Thu, 09 Oct 2008 23:35:58 +0400, BCS <ao pathlink.com> wrote:Reply to Walter,This has been done in D already:
It has some interesting features :) (I might have to steal a few for my version)
I still think my version is superior (it handled floating point powers, supports an arbitrary number of basic units (given that they are orthogonal) and allows adding them with no original code modification) :p
Oct 09 2008
Reply to Denis,On Thu, 09 Oct 2008 23:35:58 +0400, BCS <ao pathlink.com> wrote:Reply to Walter,This has been done in D already:
my version)
powers,
Re: FP, I would count that as worse than basic integers because it runs the risk of FP rounding errors. Mine will handle rational powers (1/2, 23/43, etc) and won't suffer from the loss of precision. The only places I have ever seen non rational exponents in use are in data fitting applications and just switching to a close enough rational is as good as anything there. Also, the non rational cases where mine might suffer little are the cases where FP problem are /most/ likely to crop up.supports an arbitrary number of basic units (given that they are orthogonal) and allows adding them with no original code modification) :p
You may have me on for that point, but extensibility also has it's down side; different people add the same dimension independently and then someone wants to mix them. The 5 dimensions I picked will cover almost all that cases for just about anyone. I think we each picked a different set of design choices and created a solution for them. I do think that mine is better than yours by the criteria I'm using. I can see legitimate criteria where yours is better.
Oct 09 2008









"Nick Sabalausky" <a a.a> 