[GSoC Proposal] Statically Checked Measurement Units

Cristi Cobzarenco cristi.cobzarenco at gmail.com
Mon Mar 28 11:48:45 PDT 2011


Thanks a lot for your quick response, I'm glad somebody actually read
through the whole thing and that I got some feedback. I'll try and do my
best to answer your questions.

On 28 March 2011 19:15, spir <denis.spir at gmail.com> wrote:

> On 03/28/2011 05:43 PM, Cristi Cobzarenco wrote:
>
>> Thus, the requirements for the unit system would be:
>> 1. One line definition of new units.
>> 2. Simple, yet safe and explicit conversion between units.
>> 3. Zero runtime overhead.
>> 4. Minimal extra coding effort to use units.
>>
>
> I like your study very much. Measurement units are, for me, kinds of
> conceptual types --as opposed to primitive types. I think they would be
> welcome, possibly for Phobos.
>
>  [...]
>>
>> I propose using types for base units and mixins to define derived units.
>>  One can use the typenames of the units in arithmetic operations this way:
>>
>> struct metre  {}
>> struct second {}
>>
>> void f() {
>> Quantity!("metre")        dist1 = quantity!(3.0, "metre");
>> Quantity!("metre^2")      area  = dist1 * dist1;
>> Quantity!("metre/second") speed = dist1 / quantity!(2.0, "second");
>> }
>>
>
> I think (not sure) this could be even simpler:
>  1. Thank to D's very used auto feature, avoiding double specification of
> the type; you could get rif of quantity, if it's only a convenience factory
> func.
>  2. I guess it's not needed for the user to give a quantity type to
> /constants/ (here 2.0). It is /meant/ to be a a number of seconds. This, as
> opposed to measure /variables/.
> Thus, we possibly could have:
>

I know about the 'auto' feature, and you're right, it would make more sense
use it in this situation, I just wanted to make the type of the variables
clearer. If one wants to have a data member with a measurement unit, one
needs to know the type of the object. But, you're right, that is definitely
how you would do it.
Regarding, the factory function, I was thinking that for convenience (not
write two pairs of parenthesis) we could have the function. Maybe using the
constructor is a better idea, we could provide both.
The system should definitely be able to figure out the type of dist1 /
Quantity!("second")(2.0) or dist1*dist1 automatically, you're right.
About constants. How would the system know that 2.0 is supposed to be
seconds? Imagine the following situation:

auto distance = Quantity!("metre")(3.0);
auto mass = Quantity!("kilogram")(2.0);
auto speed = Quantity!("metres/second")(mass/2.0);  // oops, the programmer
meant distance/2.0

Now the system would automatically infer that the unit for 2.0 is
"metre/(second*kilogram)" to get metres/second and the whole point of units
would be defeated. My idea is to only allow adimensional initializers
(floats, doubles etc.) for the quantites, and specify the unit for the
constants. So there would be no need to do Quantity!("meters/second"). One
could simply say:

auto speed    = distance / Quantity!("second")(2.0);

One final note about 'auto'. If a programmer abuses auto, then the part of
the reason to have units is lost. In the above example, if one simply used:

auto speed = mass / Quantity!("second")(2.0);

Then this would not fail here (it might fail later, or not at all), since
there is no unit checking performed. I think that ideally one should avoid
using auto when initializing from a result of an operation between two other
variables.


>    auto dist1 = Quantity!("meter")(3.0);
>    auto area  = Quantity!("metre^2")(dist1 * dist1);
>    auto speed = Quantity!("metre/second")(dist1 / 2.0);
>
> (Unsure whether your system could automagically infer the unit of area. ?)
>
> I'm not fan of using strings as unit identifiers. Why don't you want to use
> use the corresponding types (structs or classes) themselves?
>
>     auto dist1 = Quantity!Meter(3.0);
>
> Thus requiring each unit to be identified by a (possibly empty) struct or
> class. This also gives the opportunity for the user to actually implement
> some useful stuff in there; or to *reuse* existing types for
> things-to-be-counted (in the latter case, measures would actually be integer
> /counts/); see your Widget vs Gadget example below).
>
>
I agree that using strings as unit identifiers is a bad idea. I'm not sure
if I was clear when I said I wanted to use mixins for the units. The strings
would be converted (using string mixins) to actual type names, therefore you
wouldn't be able to use any string for a unit - it has to be the name of a
type. The reason to use strings is to be able to write derived units easily.
"Quantity!(Meter/Second)(3.0)" is not valid D if Meter and Second are types,
right? So, if you wanted to use the type names you would have to do
"Quantity!( UnitDivison!(Meter,Second) )(3.0)". An idea would be to allow
both. When there is a single type involved, you could write
Quantity!Meter(3), but when one wants to get a derived unit, one could use a
string "Meter*Second^-1" - this would behind the scenes call a function like
UnitDivision, of course. What do you think?


>
>  Conversion between units can be done specifying a single factor with a
>> proper unit:
>>
>> template conversion( alias unit : "kilometer/meter" ) {
>> immutable Quantity!(unit) conversion = quantity!(123.0,unit);
>> }
>>
>
> Unsure of the right approch here. All we need is a registered conversion
> factor for every needed (U1,U2) pair. It could be a kind of 2D associative
> array with unit keys and float values. Then, would it be possible to have a
> single convert template, like:
>
>    Quantity!U2 convert (string U1, string U2) (Quantity!U1 measure) {
>        auto factor = conversionFactors[U1][U2];   // throws if undefined
>        return Quantity!U2(measure * factor);
>
>    }
>
> ?
>
>  void f() {
>> Quantity!("metre")     d1 = quantity!(123.0,"metre");
>> // convert calls conversion! with the right argument
>> Quantity!("kilometre") d2 = convert!(d1,"kilometre");
>> }
>>
>
>   auto d1 = Quantity!("metre")(123.0);
>   auto d2 = convert!(d1,"kilometre");
> or using my approach:
>   auto d2 = convert!("metre","kilometre")(d1);
>
> Here the type for d2 is really unnecessary.


This would not be static any more, since the lookup would have to be done at
runtime, right? If the conversion template function is used, then the code
simply gets replaced by the Quantity!(measure*factor,"kilometre") at compile
time.
There is another argument for using the conversion template function. One
could only define conversion for single units (e.g. kilometres->metres,
minutes->seconds), and then conversion should be able to automagically
determine the conversion between "kilometres/minute" to "meters/second", the
factor being a multiplication of two other factors.. Again, all of this at
compile time. Does it make sense? Or am I missing something?



>
>
>  Also, notice this approach imposes no restriction to the types that define
>> units, therefore our Widget/Gadget counters could be defined without any
>> extra work:
>>
>> class Widget { /* complicated class definition */ }
>> class Gadget { /* complicated class definition */ }
>>
>> Quantity!("Widget",int) nWidgets;
>> Quantity!("Gadget",int) nGadgets;
>>
>
> See note about identifying units above.
>
> Denis
> --
> _________________
> vita es estrany
> spir.wikidot.com
>
>
Thanks again and I'm looking forward to hearing back from you.


-- 
(Cristi Cobzarenco)
Pofile: http://www.google.com/profiles/cristi.cobzarenco
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20110328/c79b0ad6/attachment-0001.html>


More information about the Digitalmars-d mailing list