Skip to content
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

Conversion between Equivalent quantities with different underlying types #293

Open
jacg opened this issue Mar 25, 2022 · 8 comments
Open

Comments

@jacg
Copy link
Contributor

jacg commented Mar 25, 2022

(Is opening issues here, appropriate for asking questions about usage of uom? Is there some better place for discussions or asking for help?)

In my uom-less code, I have something that amounts to:

let a: f32 = todo!();
let b = a.floor();
let c: usize = b as usize;

Let's say that a, b and c are all Lengths. How should this code be made unit-aware? I start off with something like this:

use uom::si::f32::Length as Flength;
use uom::si::usize::Length as Ulength;

let a: Flength = todo!();
let b = a.floor();
let c: Ulength = ??? b ???;

as cannot work for non-primitives, and my attempts to use any of the From machinery result in very noisy compiler error messages which amount to

the trait `From <Quantity ... f32 ... f32 ...> is not implemented for `Quantity ... usize ... usize ...`

Going off on a tangent, do you have any words of wisdom regarding extracting the signal from the almost pure noise in such error messages?

@jacg
Copy link
Contributor Author

jacg commented Mar 25, 2022

Of course, it is possible to do something like

use uom::si::length::meter as arbitrary_conversion_unit;

Ulength::new::<arbitrary_conversion_unit>(b.get::<arbitrary_conversion_unit>() as usize);

but I'm hoping that there's a less ad-hoc, higher-level approach available.

@adamreichold
Copy link
Contributor

adamreichold commented Mar 25, 2022

Note that the fields of Quantity are public, so if have you two quantities in the same system of units where only the base type is different, you can do something like

pub fn as_discrete_length(value: FLength) -> ULength {
    Quantity {
        dimension: PhantomData,
        units: PhantomData,
        value: value.value as usize,
    }
}

and possibly a generic extension by using some trait like num_traits::cast::NumCast to abstract over the conversion of the inner value.

@iliekturtles
Copy link
Owner

Posting questions as issue is the best way right now. It gives us a public record for anyone who may have a similar question in the future, allows others like to respond (@adamreichold has been right on this), and is, in some ways, an indication that documentation could be improved. I also haven't taken the time to see about enabling GitHub discussions (feedback on this last part welcome).

Both of the solutions mentioned so far work short-term, but I think long term the right solution is a conversion trait that takes advantage of num_traits::cast to do the dirty work. QuantityCast perhaps:

pub trait QuantityCast: ... {
    fn from<...>(q: ...) -> Option<Self>;
}
use uom::si::f32::Length as Flength;
use uom::si::usize::Length as Ulength;

let a: Flength = todo!();
let b = a.floor();
let c: Ulength = Ulength::from(b);

@iliekturtles
Copy link
Owner

Going off on a tangent, do you have any words of wisdom regarding extracting the signal from the almost pure noise in such error messages?

Realized I never responded to this. Unfortunately I don't have much wisdom to impart. My strategy is to try ignoring everything between the <...>s and hoping the surrounding descriptive text is sufficient.

@jacg
Copy link
Contributor Author

jacg commented Mar 29, 2022

One one occasion, IIRC, the only relevant information anywhere in the error message was the presence/absence of a single & buried deep inside the <...>s of two different parts of the error message.

On another, the information was the difference between usize/f32 buried deep within.

Perhaps more obviously, when the error concerns a mismatch between quantities, if you understand how typenum encodes its numbers then it's not conceptually difficult to decipher what the dimensions are, but it is time consuming and dislodges vital information from short-term memory, disrupts flow, etc.

I wonder whether it would be feasible to implement a parser and translator---in the spirit of tnfilt, but more featureful and specific to uom--- that can be fed these messages and spit out versions with the Quantities translated into human-readable form. IOW, something that can parse this:

Quantity<(dyn Dimension<L = PInt<UInt<UTerm, B1>>, J = Z0, Kind = (dyn Kind + 'static), N = Z0, T = NInt<UInt<UInt<UTerm, B1>, B0>>, M = Z0, Th = Z0, I = Z0> + 'static), (dyn uom::si::Units<f32, luminous_intensity = uom::si::luminous_intensity::candela, mass = uom::si::mass::kilogram, length = uom::si::length::meter, time = uom::si::time::second, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, amount_of_substance = uom::si::amount_of_substance::mole, electric_current = uom::si::electric_current::ampere> + 'static), f32

and turn it into something like this:

Quantity<m^1 s^-2, Kind, f32>

maybe with an option for including the base units.

@iliekturtles
Copy link
Owner

Does tnfilt work with uom? Is the resulting output still too verbose?

@jacg
Copy link
Contributor Author

jacg commented Mar 30, 2022

tnfilt (at least the slightly patched version I submitted in a PR (it looks like rustc has changed the amount of qualification in shows in the error messages, since tnfilt was written) which has been ignored thus far) does work with uom, but it doesn't help much. For example, it turns this

Quantity<(dyn Dimension<L = PInt<UInt<UTerm, B1>>, J = Z0, Kind = (dyn Kind + 'static), N = Z0, T = NInt<UInt<UInt<UTerm, B1>, B0>>, M = Z0, Th = Z0, I = Z0> + 'static), (dyn uom::si::Units<f32, luminous_intensity = uom::si::luminous_intensity::candela, mass = uom::si::mass::kilogram, length = uom::si::length::meter, time = uom::si::time::second, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, amount_of_substance = uom::si::amount_of_substance::mole, electric_current = uom::si::electric_current::ampere> + 'static), f32>

into this

Quantity<(dyn Dimension<L = PInt<U1>,              J = Z0, Kind = (dyn Kind + 'static), N = Z0, T = NInt<U2>,                        M = Z0, Th = Z0, I = Z0> + 'static), (dyn uom::si::Units<f32, luminous_intensity = uom::si::luminous_intensity::candela, mass = uom::si::mass::kilogram, length = uom::si::length::meter, time = uom::si::time::second, thermodynamic_temperature = uom::si::thermodynamic_temperature::kelvin, amount_of_substance = uom::si::amount_of_substance::mole, electric_current = uom::si::electric_current::ampere> + 'static), f32>

(alignment mine) which is almost nothing on the scale of the total amount of noise.

All it does is things like

  • PInt<UInt<UTerm, B1>> -> PInt<U1>
  • NInt<UInt<UInt<UTerm, B1>, B0>> -> NInt<U2>

If all that noise can be reduced to something like this Quantity<m^1 s^-2, Kind, f32>, then it would be really useful. I'll have a go, and see how close I can get to that ideal, but progress will be very slow, for lack of time.

@iliekturtles
Copy link
Owner

Thanks for the initial analysis. I split the request into a separate issue in case anyone gets the time and desire to implement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants