[GSoC Proposal] Statically Checked Measurement Units

spir denis.spir at gmail.com
Tue Mar 29 12:14:40 PDT 2011


On 03/29/2011 03:49 PM, Cristi Cobzarenco wrote:
> To David:
> Ok, right now, I got two working versions, one sorting by .mangleof and one
> performing a double-inclusion test on the tuples. Both work, I can't see any
> performance increase in the .mangleof one, but if .mangleof returns unique
> string, I say we use it this way.
> Regarding my string little DSL. I have 3 solutions right now:
> 1. Drop the DSL altogether, right now my system would work perfectly fine
> with boost-like tuples (a list of units alternating with exponents):
> Quantity!(Metre,1,Second,-1) speed = distance/time;
> While less readable, this doesn't have the disadvantages of the following 2.
>
> 2. Use a mixin template to declare the expression parser in the current
> scope:
> mixin DeclareExprQuantity!();
>
> struct Metre {}
> struct Second {}
> struct Kg {}
>
> void f() {
>         ExprQuantity!("Metre/Second * Kg^-1") q = speed / mass;
> }
> This works, is readable, but it uses C-preprocessor like behaviour (read:
> black vodoo) - a library declaring something in your scope isn't very nice.
>
> 3. Abandon using types as units and just use strings all the way. This
> doesn't guarantee unit name uniqueness and a misspelled unit name is a new
> unit. One could use an algorithm to convert all strings to a cannonical form
> (like Andrei suggested) and then use string equality for unit equality.
>
> What do you think, I'm personally quite divided:
> 1. I like that this is simple and it works. It make writing derived units
> unnatural though.
> 2. I actually like this one, despite the obvious ugliness. It's just one
> extra line at the beginning of your code and you can the use arithmetic
> operations and use type-uniqueness to guarantee unit-uniqueness.
> 3. This is a bit dangerous. It works very well as long as there isn't more
> than one system of units. I still like it a bit.

Have you considered
0. Derived units are declared?

After all, relative to the size of an app, and the amount of work it 
represents, declaring actually used derived units is very a small burden.

This means instead of:

     struct meter {}
     struct second {}

     auto dist = Quantity!"meter"(3.0);
     auto time = Quantity!"second"(2.0);
     auto speed = Quantity!"meter/second"(dist/time);
     auto surface = Quantity!"meter2"(dist*dist);

one would write:

     struct meter {}
     struct second {}
     alias FractionUnit!(meter,second) meterPerSecond;
     alias PowerUnit!(meter,2) squareMeter;

     auto dist = Quantity!meter(3.0);
     auto time = Quantity!second(2.0);
     auto speed = Quantity!meterPerSecond(dist/time);
     auto surface = Quantity!squareMeter(dist*dist);

This means you use struct templates as unit-id factories, for user's 
convenience. The constructor would then generate the metadata needed for 
unit-type checking, strored on the struct itself (this is far more easily using 
such struct templates than by parsing a string).
In addition to the 2 struct templates above, there should be
     struct ProductUnit(Units...) {...}
(accepting n base units); and I guess that's all, isn't it?
The only drawback is that very complicated derived units need be constructed 
step by step. But this can also be seen as an advantage. An alternative may be 
to have a single, but more sophisticated and more difficult to use, struct 
template.

I find several advantages to this approach:
* Simplicity (also of implementation, I guess).
* Unit identifiers are structs all along (both in code and in semantics).
* No string mixin black voodoo.

I guess even if this is not ideal, you could start with something similar, 
because it looks easier and cleaner (to me).

A similar system may be used for units of diff scales in the same dimension:
     alias ScaleUnit!(mm,1_000_000) km;

By the way, have you considered unit-less (pseudo-)magnitudes (I mean ratios, 
including %). I would have one declared and exported as constant. then,
     alias ScaleUnit!(voidUnit,0.001) perthousand;


> To Don:
> * Choosing one unit and using it is still a very good idea. As I said there
> are to be no implicit conversions, so this system would ensure you don't, by
> mistake, adhere to this convention. Also, if somebody else uses your library
> maybe they assume everything is in meters when in fact you use milimeters.
> Sure they should check the documentation, but it's better if they get a nice
> error message "Inferred unit Meter doesn't match expected Milimeter", or
> something like that.

I agree with this.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com



More information about the Digitalmars-d mailing list