librasteve / raku-Physics-Measure

do calculations on objects with value, units and error
Artistic License 2.0
11 stars 2 forks source link

Error in Unit conversion Velocity = Length / Time #29

Closed rcmlz closed 3 years ago

rcmlz commented 3 years ago

When running the code below, the velocity is not calcualted from time and length.

Error: "cannot convert in to different type Length" in method in at /opt/rakudo-star-2020.01/install/share/perl6/site/sources/E184BD56357B91198F066C3A2CD14FEB4D0BDB42 (Physics::Measure) line 199 in method rebase at /opt/rakudo-star-2020.01/install/share/perl6/site/sources/E184BD56357B91198F066C3A2CD14FEB4D0BDB42 (Physics::Measure) line 206 in method divide at /opt/rakudo-star-2020.01/install/share/perl6/site/sources/E184BD56357B91198F066C3A2CD14FEB4D0BDB42 (Physics::Measure) line 153 in sub infix:</> at /opt/rakudo-star-2020.01/install/share/perl6/site/sources/E184BD56357B91198F066C3A2CD14FEB4D0BDB42 (Physics::Measure) line 477 in block at test.pl6 line 6

Environment: "Rakudo version 2020.01 built on MoarVM version 2020.01.1 implementing Perl 6.d."

<----------------------> use Physics::Measure; use Physics::UnitPostfix;

my $s = Measure.new(value => 5.1, units => 'm'); say ~$s; my $t = Measure.new(value => 2.3, units => 's'); say ~$t;

my $v = $s / $t; say ~$v; <---------------------->

librasteve commented 3 years ago

Hi @IlRoccOne - thank you for trying the Physics::Measure module. The intent is this...

use Physics::Measure;
use Physics::UnitPostfix;

my $s = Length.new(value => 5.1, units => 'm'); say ~$s;
my $t = Time.new(value => 2.3, units => 's'); say ~$t;

my $v = $s / $t; say ~$v;

to give this

5.1 m
2.3 s
2.217391 m/s
librasteve commented 3 years ago

I see that this is a 'misdirect' in the synopsis and have (hopefully) corrected this. What's going on under the hood is ...

class Length is Measure { ... }
class Time is Measure { ... }
#and so on

In general you want the child class (Length, Time ...) and not the parent class (Measure) as this let's the module do its magic when you perform math on the objects, knowing that (Speed) = (Length) / (Time) and so on...

You can try this:

my $s = 5.1m;  say ~$s;
my $t = 2.3s;   say ~$t;

my $v = $s / $t; say ~$v; say $v.WHAT;

to give this:

5.1 m
2.3 s
2.217391 m/s
(Speed)

NB. Sorry this has unearthed a real bug in the module - please go zef uninstall Physics::Measure then zef install --verbose https://github.com/p6steve/raku-Physics-Measure.git before this will work...

rcmlz commented 3 years ago

When running

use Physics::Measure; use Physics::UnitPostfix;

my $s = 5.1m; say ~$s; my $t = 2.3s; say ~$t;

my $v = $s / $t; say ~$v; say $v.WHAT;

I get the error message "Confused". However, when running

use Physics::Measure; use Physics::UnitPostfix;

my $s = 5.1mm; say ~$s; my $t = 2.3ms; say ~$t;

my $v = $s / $t; say ~$v; say $v.WHAT;

It works as expected. So I conclude the error lies in the handling of "Not having a prefix to the base unit", as "mm" works, but "m" not.

rcmlz commented 3 years ago

In general I prefer using

my $s = Measure.new(value => 5.1, units => 'm');

over the available short cuts (my feedback to "libra" would be rather "reserved"), as the full specification of a measurement value would allow to make the module much more powerfull, e.g. by introducing errors bands and significance levels. When using physical measurements in any sort of physical formulas it would be (especially in academic context) helpfull to show (to students) that e.g.

my $s = Measure.new(value => 1.234567, units => 'm', error => '1%'); my $t = Measure.new(value => 5.0, units => 's', error => '10%');

is not 0.246913 m/s as the calculator shows when you just run 1.234567 / 5.0

librasteve commented 3 years ago

Hi @IlRoccOne

Once again, many thanks for persisting with this and for your great feedback!!

(1) on the 'mm' vs 'm' point - this is a real bug that crept in (sorr y)- I hot-fixed this yesterday ... but have not yet released to CPAN so you please follow the re-install directly from the Master branch of this GitHub repo ... zef uninstall Physics::Measure then zef install --verbose https://github.com/p6steve/raku-Physics-Measure.git before this will work...

(2) I 100% agree about errors and this will be added to the module in a future release ... the initial f/back I got was that the Postfix notation my $x = 20ml; e.g. to give Volume.new( value => 20, units => 'ml' ); was higher priority ... so that was the last big addition to this module.

(3) I think we are slightly at cross purposes on the class model - the underlying design intent is:

class Unit {
   has Str $.defn;
   #... 
}
class Measure {
   has Real $.value;
   has Unit $.units;
   has Error $.error;  #version 2
   #...
}
class Length is Measure {}
class Time is Measure { ... }
class Speed is Measure {}
#and so on

The benefit of all this is that you can do math on Measure objects - Length can add/subtract to Length, Time can add/subtract to Time, but adding a Length to a Time gives a raku type error. I can, however, divide Length by Time and then I get a Speed type back. Length * 3 => Volume. And so on. Your students may want to know what you get when you go: ```my $w = $s / ( $t $t ); say ~$w; say $w.WHAT;``` for example. ;-)

Therefore you are not getting the main benefit of Physics::Measure if you just make Measure objects per your example. You need to be making the child objects. I SEE THAT THIS IS A WEAKNESS OF THE MODULE DOCUMENTATION AND I WILL FIX THIS AS SOON AS I CAN. ;-)

(4) Right now there are 3 ways provided to make child objects: (a) my Length $s = Length.new(value => 5.1, units => 'm'); which is good raku (and OO in general), but is quite long hand where you want to work with many Measure objects in your code. . (b) Thus the 'libra' notation came about as a short hand trick to cut out the constructor boilerplate that will take a unit Str and auto 'look up' the child type thusly my $s ♎️ '5.1m'; say $s.WHAT; #(Length) (c) The Postfix notation does a similar but different trick using raku Postfixes my $s = 5.1m; say ~$s.WHAT; this is super handy BUT this is limited to common combinations of SI prefixes and unit names for performance reasons.

So, while I agree it is a little odd with the ♎️ emoji code, it is a very quick way to do something like my $s ♎️ '5.1 feet ±2%'; say ~$s.in('m'); Basically it's a

In summary - very much appreciate the feedback ... I hope that you are in tune with the class model benefits. My main use cases are teaching (thus having most non SI in the mix for historical aspects) and to provide a scientific / navigation / engineering calculator tool.

I am wondering how best to balance (b) and (c) ... based on your (and other) f/back probably I will push the emoji option way down in the doc as there are many other concepts to convey first...

Meantime I have reopened this ticket and will keep open as a reminder to fix the docs for v0.0.4.

rcmlz commented 3 years ago

Thank you for the explanation. Now I get it. I was assuming that Measure.new() would derive from the "unit" what child class this variable belongs too. Making it explicit works as expeced. Thank you.

use Physics::Measure; use Physics::UnitPostfix;

my $s = Length.new(value => 5.1, units => 'm'); say ~$s; my $t = Time.new(value => 2.3, units => 's'); say ~$t; my $v = $s / $t; say ~$v; say $v.WHAT;

Having proper (automated) error bandwith (also from imported physical constants) and (automated) significance level rounding would be very helpfull - of course with switch on/off to show the difference and have a nice didactic pathway to go from "as typed in the calculator" to the correctly rounded answer with correct units and measurement error bandwith attached. Perhaps enhancing Math::Model with Physics::Measure to create Physics::Formula or Physics::Model ... ? Just some thoughts ...