On 3/29/11 3: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.

To be honest, I still don't see how you are able to get away without canonicalization in the first place; would you mind to elaborate on how you solve the issue of different ordering of expression yielding types? This is not about the algorithm to determine whether two types are semantically equivalent, where your algorithm would work fine as well, but about the actual D types. If you don't sort them, Quantity!(BaseUnitExp!(Meter, 1), BaseUnitExp!(Second, -2)) and Quantity!(BaseUnitExp!(Second, -2), BaseUnitExp!(Meter, 1)) would be different types, which is not desirable for obvious reasons.


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.

[…]

The only completely clean alternative would be the abominable:
Quantity!( mixin(Expr!("Metre/Second")) ) q;

Get out of my head! Immediately! ;) Just kidding – incidentally I considered exactly the same options when designing my current prototype. My current approach would be a mix between 1 and 2: I don't think the Boost approach of using »dummy« instances of units is any less readable than your proposed one when you don't deal with a lot of units. For example, consider

enum widgetCount = quantity!("Widget")(2);
vs.
enum widgetCount = 2 * widgets;

This could also be extended to type definitions to avoid having to manually write the template instantiation:

Quantity!("meter / second", float) speed;
vs.
typeof(1.f * meter / second) speed;

There are situations, though, where using unit strings could considerable improve readability, namely when using lots of units with exponents. In these cases, a mixin could be used to bring all the types in scope for the »parsing template«, similar to the one you suggested. If a user of the library things could use an additional mixin identifier to clarify the code, e.g. »mixin UnitStringParser U; […] U.unit!"m/s"«).

But a more attractive solution would exploit the fact that you would mostly use units with a lot of exponents when working with a »closed« unit system without the need for ad-hoc extensions, like the SI system, which would allow you to use unit symbols instead of the full name, which wouldn't need to be globally unique and wouldn't pollute the namespace (directly defining a type »m« to express meters would probably render the module unusable without static imports).

It would essentially work by instantiating a parser template with all the named units. Thus, the parser would know all the types and could query them for additional properties like short names/symbols, etc. In code:

---
module units.si;
[…]
alias UnitSystem!(Meter, Second, …) Si;
---
module client;
import units.si;
auto inductance = 5.0 * Si.u!"m^2 kg/(s^2 A^2)";
---

This could also be combined with the mixin parser approach like this:
---
import units.si;
mixin UnitStringParser!(Si) U;
---

But to reiterate my point, I don't think a way to parse unit strings is terribly important, at least not if it isn't coupled with other things like the ability to add shorthand symbols.

David

Reply via email to