digitalmars.D - Units of Measure in F#
- bearophile (9/9) Oct 06 2008 I have found this interesting old thread, I don't know how much those th...
- Nick Sabalausky (3/16) Oct 06 2008 That's awesome. I want it!
- BCS (13/33) Oct 06 2008 I think it is fully doable right now. I've considered doing it off and o...
- Denis Koroskin (6/19) Oct 06 2008 Arghh! I've almost done the trick with D templates and then I got this:
- BCS (6/39) Oct 06 2008 svn.dsource.org seems to be having problmes or I'd post a system I just...
- bearophile (6/11) Oct 07 2008 A lot of work. Does is use a (syntax) strategy similar to this?
- BCS (5/20) Oct 07 2008 SVN is working again (or I'm somewhere it works from)
- bearophile (4/6) Oct 07 2008 I think there's a need of some syntactic sugar :-)
- BCS (3/12) Oct 07 2008 I put up some better examples.
- Denis Koroskin (60/78) Oct 07 2008 Well, I have done it completely different. Here is my (simplified) class...
- ore-sama (1/1) Oct 07 2008 Hmm... do we want to add pressure to energy density?
- Walter Bright (253/253) Oct 08 2008 This has been done in D already:
- BCS (2/3) Oct 09 2008 It has some interesting features :) (I might have to steal a few for my ...
- Denis Koroskin (4/8) Oct 09 2008 I still think my version is superior (it handled floating point powers, ...
- BCS (16/31) Oct 09 2008 Re: FP, I would count that as worse than basic integers because it runs ...
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, bearophileThat'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, bearophileI 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, bearophileArghh! 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: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 rootI 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, bearophileArghh! 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
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 rootA 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 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)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 rootA 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
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:I put up some better examples. http://www.dsource.org/projects/scrapple/browser/trunk/units/si.dtake 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
On Tue, 07 Oct 2008 22:39:10 +0400, BCS <ao pathlink.com> wrote:Reply to bearophile,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;BCS: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)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 rootA 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
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,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) :pThis has been done in D already:It has some interesting features :) (I might have to steal a few for my version)
Oct 09 2008
Reply to Denis,On Thu, 09 Oct 2008 23:35:58 +0400, BCS <ao pathlink.com> wrote: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.Reply to Walter,I still think my version is superior (it handled floating point powers,This has been done in D already:It has some interesting features :) (I might have to steal a few for my version)supports an arbitrary number of basic units (given that they are orthogonal) and allows adding them with no original code modification) :pYou 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