synek317 / synonym

Another try to simplify newtype creation in rust
MIT License
13 stars 1 forks source link

Support arithmetic operations between new types and underlying types #4

Open umut-sahin opened 2 months ago

umut-sahin commented 2 months ago

Would be cool to be able to do:

#[derive(Synonym)]
pub struct MyInt(i32);

let x = MyInt(10);
let y = x + 1; // y: MyInt
synek317 commented 2 months ago

I wonder if such a feature can be added "just like that". For some "synonyms" maths can work differently, e.g. if you do

#[derive(Synonym)]
pub struct Seconds(f64);

then you should be able to multiply seconds by a number and expect the result to be seconds and rather avoid multiplying seconds by seconds.

OTOH this crate is not designed to create a unit of measurement. I will think about it.

umut-sahin commented 2 months ago

I don't think the scope of the crate needs to be expanded to that. Already there are:

To me this crate is just creating new types to be used in signatures to avoid bugs (or in my case, to use it for my Bevy components) and this feature request is to make that easier to work with.

#[derive(Synonym)]
pub struct Speed(pub f64);

pub fn double_player_speed(player_query: Query<&mut Speed, With<Player>>) {
    let mut player_speed = player_query.single_mut();
    player_speed *= 2.00;
    // is better then
    // player_speed *= Speed(2.00);
    // or
    // player_speed.0 *= 2.00;
}

or

#[derive(Synonym)]
pub struct Health(pub f64);

pub fn increment_player_health(player_query: Query<&mut Health, With<Player>>) {
    let mut player_health = player_query.single_mut();
    player_health += 1.00;
    // is better then
    // player_health += Health(1.00);
    // or
    // player_health.0 += 1.00;
}

or

#[derive(Synonym)]
pub struct Health(pub f64);

pub fn player_death(player_query: Query<&Health, With<Player>>) {
    let player_health = player_query.single();
    if player_health <= 0.00 { ... }
    // is better then
    // if player_health <= Health(0.00) { ... }
    // or
    // if player_health.0 <= 0.00 { ... }
}

(this one might require an explicit deref with * but you get the idea)

But this is just a minor inconvenience, not a blocker, so it's all good either way.

umut-sahin commented 2 months ago

If you prefer to go with units, maybe you can:

Units wouldn't get mixed and most of the functionality would be retained (except for multiplying or dividing two Speeds for example).

Wait, actually you can support division as well, it might result in the underlying type:

let x = Speed(3.00);
let y = Speed(6.00);
assert_eq!(x / y, 0.50);  // x / y is an actual f64

It can be useful for calculating proportions.

So yeah it looks pretty good with just disabling multiplication between new types and changing the result of division :stuck_out_tongue:

synek317 commented 2 months ago

Yes, I'm not aiming for a units lib. You got the point of the lib very well with

To me this crate is just creating new types to be used in signatures to avoid bugs (or in my case, to use it for my Bevy components)

However, I guess you already see my point that it makes more sense to multiply the speed by a float and not by another speed. Perhaps supporting addition and subtraction is very straightforward. RE multiplication/division - since the crate version is still at 0.x, I could perhaps experiment with implementing newtype [*/] underlying type resulting in a newtype and give configuration to change the behaviour.

By intuition, this is the most common case.

umut-sahin commented 2 months ago
To me, the best approach is this: Operation With New Type With Underlying Type
Add/Sub ✓ -> New Type ✓ -> New Type
Eq/PartialEq/Ord/PartialOrd ✓ -> bool ✓ -> bool
Mul x (as it would change the unit) ✓ -> New Type
Div ✓-> Underlying Type (to calculate proportion) ✓-> New Type
AddAssign/SubAssign
MulAssign/DivAssign x (as it would change the unit)

It doesn't mess with the unit and provide as much functionality as possible!

umut-sahin commented 2 months ago

Btw, I'd love to help with some of the issues I've created if you're interested. I think this is a really cool library and it'd massively improve some of the headaches of working with the new type paradigm.