Closed paholg closed 8 years ago
I think changing the type aliases is a good idea. See my reply to #8. Derived units are going to need review whatever way they are defined. type Newton SI<P1, P1, N2>
is as easy to mess up at derived!(Meter * Kilogram / Second / Second)
. If the most commonly used quantities are already defined within the library and are going through a code review process then ergonomics isn't as important since each library user wouldn't be asked to redefine Force/Newtons.
I'm working on the alias switch, and it's really nice for base units. You can even do things like
let x = Meter::new(3.0);
which I think is a big win.
The problem is it breaks down for derived units, due to Rust's issue of not normalizing associated types in time. Worst case scenario, we can go with a build script, but I'm gonna look into procedural macros and see if there's anything there that can save it.
I am restructuring dimensioned from the ground up. Most importantly, what used to be Dim<SI<Meter, Second, ...>, V>
is now SI<A, V>
where A
is a type-level array of type-numbers.
This carries with it a few advantages.
One, when one defines a unit system, they are creating the outermost type, and so no one has to worry about orphan rules for unit systems they define. I consider this a big plus.
Two, it lets us use std::convert::From
for conversions! I am very excited about this.
Here is an example of a conversion from a meter, kilogram, second system to a centimeter, gram, second system:
impl<V, Meter, Kilogram, Second> From<mks::MKS<V, tarr![Meter, Kilogram, Second]>>
for CGS<Prod<V, f64>, tarr![Meter, Kilogram, Second]> where
Meter: Integer, Kilogram: Integer, Second: Integer,
V: Mul<f64>,
{
fn from(other: mks::MKS<V, tarr![Meter, Kilogram, Second]>) -> Self {
// Note we have to be a bit careful here because these unit systems are special and use
// double the regular unit power, so that they can be represented with half integer
// powers. E.g. The unit for area will really be `m^4`.
let mfac = 100.0f64.powf(Meter::to_i32() as f64 / 2.0);
let kgfac = 1000.0f64.powf(Meter::to_i32() as f64/ 2.0);
// Both systems use seconds for time, so no need to do anything with it!
// let sfac = 1.0f64.powi(Meter::to_i32());
let fac = mfac * kgfac;
CGS::new( other.value * fac )
}
}
With the old system, I couldn't get any conversion to a level of usability that I was satisfied with. I can't think of any method of conversion that would be better than this way, so I see it as the biggest win from this restructuring.
The only real downside that I can think of is that pretty much the entire library is now contained in the make_units
macro, which makes implementing it and will make maintaining it a bit more unpleasant. I don't see this as a big deal though.
You can see the current changes in the rmdim
branch. You won't be able to run it, though, without local versions of typenum and generic-array, because it depends on an addition to typenum that hasn't yet been merged to master. That should happen soon, though.
Edit: fixed this!
I believe the issues here have generally been solved.
I would like using dimensioned to be as painless as possible. It should feel like you're working with primitives, only they yell at you more when you make mistakes.
If you have any thoughts on the ergonomics of dimensioned, in regards to anything, please add them here!
type aliases
Currently, we have aliases that look like this:
which requires you to use it like this:
I would like to switch the aliases to look like this:
so that the above function could be replaced with:
I am not certain that this won't cause issues in other places (especially in defining derived units, which I'm currently working on as well), but barring that I'm not 100% sure it's a good idea.
It makes dimensioned types cleaner to view and easier to type.
It makes error messages more cryptic (they won't change, but they contain
Dim
which users will no longer need to deal with directly).It also might make some things more confusing. I'm not sure exactly.
derived units
It is important for both unit systems and users of them to be able to conveniently define derived units. Right now, you can do
but that isn't great. I have a macro in progress that can turn
into
but that has flaws as well, mainly due to the typechecker not doing what we want. Basically,
Newton
won't always act like it'sSI<P1, P1, N2>
, but instead will be treated as the associated typeOutput
of those computations, even though the latter, once computed, is the former. Basically, this issue.One solution is to use a build script to generate derived types, but that doesn't help users make their own, which should be as painless as possible. Possibly this can be solved with syntax extensions, but I have no familiarity there.