-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Templify si.d #4
Conversation
Yes it works, but having to write code like below is what I wanted to avoid (assuming N == double would be the default): auto distance = 384_400 * kilo(meter!());
auto speed = 299_792_458 * meter!()/second!();
Time!() time;
time = distance / speed;
writefln("Travel time of light from the moon: %s s", time.value(second!())); An alternative would be to put all si.d code into a template mixin (with N as a parameter) and require the user to instantiate it before use. |
I think I should include the code from examples/synopsis in a proper unittest, so that expected usage is properly checked. |
I believe the standard D way of solving this is to replace enum meter(N = StdN) = unit!(N, "L"); with auto meter(N = StdN)() { return unit!(N, "L"); }; as long as it's CTFE-able. I look into this tomorrow. |
Good idea with the unittests by the way. |
It could work, but beware the consequences of having a function instead of a value; for example, Length is defined as: alias Length = typeof(meter); I wonder if forcing the numeric type to be a |
I don't think the D developers will approve a solution where precision is "forced". We want this to work with all numeric types including BigInt and user defined types. Why is it important to use |
|
Neither So you will be able to write something like auto value = 1.0 * kilogram * meter / square(second) This thanks to D not requiring function calls to arguments to include empty paren pairs, such as This will infer precision of value to be I'll cook up a PR for this in the next couple of days. Ok? |
I guessed I missed something, so I am waiting for your PR. Thanks for working on this! |
You are right. There is no optimal solution to this problem. The symbol Instead I think it's a good solution to make it enum meter = unit!(StdN, "L");
auto meters(N = StdN)(N n = 1) { return n*unit!(N, "L"); }; What do you think about the solution that adds a plurals s for the functional (templated) case? unittest
{
auto x = meter; // type double
auto xf = 2.0f.meters; // inferred to type float because 2.0f has type float
auto yf = meters!float; // inferred to type float because explicit template argument
} An alternative naming for |
IMO, auto sf = 1.0f.kilograms * 1.0f.meters / square(1.0f.seconds); Compare to this: import quantities.si.float_;
auto sf = 1.0f * kilogram * meter / square(second). I find it more expressive. How often would the same code use quantities with double, float, int or whatever at the same time? Maybe importing a specialized versions of si.d could be better. Specialized versions of si.d would only mixin a template from a 'sibase' module. A user could build its own version with the same mechanism. Fully-qualified names or aliases could resolve conflicts if they they appear. I don't know, I am just thinking out loud. |
I think these matters will become clear when we create the PR and these questions become discussed with the other Phobos developers in a more "democratic" process. There may be things we are missing. A more important feature that I'm sure the developers, especially @andralex, will want is support for 1.0*unit!(float, "L") * 2.0*unit!(double, "L") * 3.0*unit!(real, "L") should become 6.0*unit!(real, "L^3") because I can start working on this later today. |
I already tried using CommonType in a previous version, and I reverted it. Because it means every function using quantities as arguments has to be templated. Speed myFunction(Length len, Time tim) { /* ... */ } now has to be: auto myFunction(L, T)(L len, T tim)
if (AreConsistent!(L, Length) && AreConsistent!(T, Time))
{ /* ... */ } This is because there is no implicit conversion. |
Why is that a problem? Everything else in Phobos is templated. |
I was talking about client code, not library code. |
I just realized that we don't need to use I'm sorry for the reformattings. They weren't intentional. I don't think our purpose of using |
@biozic: BTW, where do you live? :) |
I live in Dijon, France. Good wines always at hand :) |
About |
About plurals in unit names: plural is ok for units in the numerator, but not in the denominator. You don't say 'meters per seconds'. A solution is to have both aliased. I don't like sentence-like naming personally, and chose to stick to the mathematical definition: quantity = value * unit. Apart from that, the PR looks good, thanks. I will test it soon. |
I'm planning to add a mixin in auto meters(N = StdN)(N n = 1) { return n * unit!(N, "L"); }
enum meter = meters!StdN; so we get both both simplicity enum x = 1.0 * meter; // only double precision
const y = 1.0 * meter; // only double precision and flexibility enum x = 1.0.meters!float; // float precision
enum y = 1.0.meters!real; // real precision |
I really don't like having different semantics between singular and plural. I would balance flexibility with simplicity this way:
enum meter(N) = unit!(N, "L"); Using it directly all the time can feel verbose: auto speed = 4.5 * kilo(meter!float) / second!float; but we just need to: enum kms = si!"km/s"; // defining a new unit, using the default double
enum kms = si!("km/s", float); // if the user wants to
Speed!float = si!"4.5 km/s"; // assignation/construction remains simple Does this contradict your plan ? :-) |
Points 1 and 2 are ok to me. I really like the syntax |
How do you use this |
We could make the 1.0.si!"km/h" What do you think I the most probable use case 1.0.si!"km/h" or x * si!"km/h" ? I think the answer to that question should decide whether we should make the symbols enums or instantiators. |
I don't know whether defining constants would be a probable use case or not. Physical constants would be defined once and for all. But I suppose they would still appear quite often in expressions like: if (speed > 1.0.si!"km/h") { /*...*/ } In this case, would For variables, I really don't know what D developers would prefer between mySpeedValue.si!"km/h" and mySpeedValue * si!"km/h" But both syntaxes are possible if Something worth checking: do all these versions generate the same code, whether in simple or complex expressions? Zero-overhead (compared to using plain numeric types) is needed. |
I got another idea. What about making shorthands such as /// Base Units
enum meter (N = StdN) = unit!(N, "L");
enum kilogram(N = StdN) = unit!(N, "M"); /// ditto
enum second (N = StdN) = unit!(N, "T"); /// ditto
enum ampere (N = StdN) = unit!(N, "I"); /// ditto
enum kelvin (N = StdN) = unit!(N, "Θ"); /// ditto
enum mole (N = StdN) = unit!(N, "N"); /// ditto
enum candela (N = StdN) = unit!(N, "J"); /// ditto
/// Base Unit Aliases
alias metre = meter; /// ditto
/// Base Units Shorthands
auto m (N = StdN)(N n) { return n * meter!N; }
auto kg(N = StdN)(N n) { return n * kilogram!N; }
auto g (N = StdN)(N n) { return n * gram!N; }
auto s (N = StdN)(N n) { return n * second!N; }
auto A (N = StdN)(N n) { return n * ampere!N; }
auto K (N = StdN)(N n) { return n * kelvin!N; }
auto cd(N = StdN)(N n) { return n * candela!N; }
unittest
{
auto m = 1.0L; // m can be overloaded as variable
auto y = m.m; // inferred as real
} But the syntax Further, we could make |
Shorthands: no. As you said, the
I won't be online often for until mid-August. Meanwhile, you can have a look at the definition of symbol and prefix lists and at runtime parsing, that will also be necessary to achieve numeric-type-agnostic 'templatization' :) We will also need tests for each functionaly for different numeric types, and for expressions where they are mixed: I can do this myself later. |
Yes, si!"km/h" works as long as we don't define an I say we go with instantiator functions. This enables good shorthands: 1.0f.si!"km/h"
1.0.si!"km/h"
1.0L.si!"km/h" for different precisions apart from the defaulted case given above. Se my latest commit for details. In the light of this, do you still want the the derived units, such as enum radian(N = stdN) = meter!N / meter!N; to be enum constants instead of instantiators? |
Ping! I'm eager to pack this into a new Phobos package What do you say? |
Replace version logic with templates.
Compiles without warnings and all tests pass.
I added a few uses of CTFE where I felt it made the code more readable.
Comments please :)