11 ms·
Lately I've been playing around with static dimensional analysis in Rust. The overall idea is similar: Use PhantomData to add a type parameter and define an emp
by jarrettc 11y ago
Lately I've been playing around with static dimensional analysis in Rust. The overall idea is similar: Use PhantomData to add a type parameter and define an empty struct for each unit. So you might end up with, say, Scalar<Newtons> or Vec3<Meters>.
Dividing and multiplying units statically is where I've had trouble so far. I think I've found a way, but it would depend on negative trait bounds, as discussed here:
https://github.com/rust-lang/rfcs/issues/1053 https://github.com/rust-lang/rfcs/issues/1053
Ideally, I'd like to be able to do something like this:
let a: Scalar<Joules> = Scalar::new(2.0);
let b: Scalar<Seconds> = Scalar::new(3.0);
let c: Scalar<Watts> = a / b;
// Watts is a type synonym for Over<Joules, Seconds>.
// Other derived units would use Times<U, V>. E.g.:
type Pascals = Times<Newton, Times<Meter, Meter>>.
- TJSomething 11y agoBut what makes Times<Times<Newton, Meter>, Meter> the same as Times<Newton, Times<Meter, Meter>>?
- dastbe 11y agoCanonicalization is tough, and requires you to define some common ordering on your units. Systems will use an array of unit powers, so that if the array were defined as <Joules, Seconds, Newtons, Meters>, then acceleration would be <0,-2,0,1> and watts would be <1,-1,0,0>. Addition and subtraction require that your arrays are equal, and multiplication and division are pairwise additive/subtractive.
- jarrettc 11y agoWill the Rust compiler ever understand numbers in types? I.e. will the numbers ever be more than just part of the string that is the type name? If not, then I don't know how possible powers will be. Maybe the solution would be to define some types for commonly-used powers, e.g. PowN4, PowN3, PowN2, PowN1, Pow2, Pow3, Pow4. Users who needed higher powers could define those types themselves, I guess.
- steveklabnik 11y agoWe do desire type-level integers, yes. There hasn't been an RFC yet, though.
- sigterm 11y agoIs it tough? There are only seven fundamental units. https://en.wikipedia.org/wiki/SI_base_unit https://en.wikipedia.org/wiki/SI_base_unit Just represent every unit in terms of them then it's good. One problem with the array you came up is that the units are not orthogonal, since Joules = Newtons * Meters.
- jcranmer 11y agoActually, there's more "kinds" of units: radians (versus degrees) and steradians come to mind.
- masklinn 11y agoThese are dimensionless derived units in SI equivalence (respectively m/m and m2/m2)
- dastbe 11y agoSorry! I never really spent much time in the physical sciences, so I didn't know that. You would obviously not want to pick Joules as a fundamental unit.
- jarrettc 11y agoGreat question. I believe each unit struct would need to implement PartialEq or something similar. That would define the canonical nesting order. Alphabetical would make sense. We would to resolve the nesting during multiplication and division. For example: let a: Scalar<Times<A, B>> = Scalar::new(10.0); let b: Scalar<Times<A, Times<B, C>>> = Scalar::new(5.0); let c: Scalar<Times<A, Times<A, Times<B, Times<C>>>> = a * b; Another challenge is that the type of c is ugly. But this could be mitigated by generous use of type synonyms.
- wyager 11y agoIf you aren't aware of it, the dimensional library for Haskell does this. https://hackage.haskell.org/package/dimensional https://hackage.haskell.org/package/dimensional