Raku / problem-solving

🩋 Problem Solving, a repo for handling problems that require review, deliberation and possibly debate
Artistic License 2.0
70 stars 16 forks source link

DivideByZero exception has conflicting attributes #434

Open coke opened 1 month ago

coke commented 1 month ago

While roast only seems to check the type of the exception, current rakudo has 3 attributes:

Checking where the exception is thrown by rakudo, it seems to either be a numerator/using pair, the single attribute details, or nothing. We should be consistent in usage and probably select numerator and using, remove details, and ensure that wherever we throw the exception, we include the information.

Because there is no testing of these attributes in roast, we could do this as an implementation detail in existing rakudo, but I would recommend implementing it as a 6.e feature and then testing the attributes in roast for 6.e

raiph commented 1 month ago

I'm on vacation, and can't yet give this full attention, but Raku's DBZ, successes, failures, DBZ exceptions, ....

librasteve commented 1 month ago

[please can you point to the source and I would be happy to try and grok the intent if I can]

for a long time IEEE P754 has given us valid values for the results of DBZ these were provided to allow pipelined vector FPU ops to succeed (even if an interim result was Inf or NaN) raku, uniquely [afaik], seeks to bridge this idea with Rats which can have a 0 denominator unless collapsed eg into a Str

following raiph's pointer to the docs on ZBRs, I see that this is OK:

say  <1/0>.Num;   # OUTPUT: «Inf␀» 
say <-1/0>.Num;   # OUTPUT: «-Inf␀» 
say  <0/0>.Num;   # OUTPUT: «NaN␀» 
say Inf.Rat.nude; # OUTPUT: «(1 0)␀» 

yet this is an error:

say <1/0>;
# Attempt to divide by zero when coercing Rational to Str in ...

my guess on the error attributes is:

coke commented 1 month ago

You can find all the instances (though you'll have to look at the source because many of these are part of a multi-line invocation) with

$ cd rakudo
$ ack 'X::Numeric::DivideByZero' src

numerator is the numerator (only ever specified with using). using is typically the operator that was called (e.g. div). There's no need to specify the denominator... because it's 0.

details is just plain text describing the situation, e.g.:

There is nothing in the current usage of rakudo or roast that requires details over the numerator/using combo. Both of the examples above are easily translatable (esp. since using is just free text)

There is no tie in to the CPU/FPU operations here.

lizmat commented 1 month ago

s/ack/rak/ ?? :-)

librasteve commented 1 month ago

@coke thanks for clarifying my guess - looks like the info I guessed at is split between the using and details field (as you say no need for denominator - red face)

Based on this, I see no issue in keeping the fields as they are since both the using (ie the operator) and the details (ie when doing what) seem useful for debugging. The ZBRs are a quite thorny and unique(?) to raku so for me at least finding any extra hint is very valuable to work out what is happening.

On the FPU, apologies if I did not explain myself carefully, but the situation I wanted to clarify (at least to my own satisfaction) is that where this is a thing:

say  <1/0>.Num;   # OUTPUT: «Inf␀» 
say  <1/0>.Num.WHAT;   # OUTPUT: «(Num)␀» 

And this (although you can't make a string from it):

say  <1/0>.Num.Rat.WHAT;   # OUTPUT: «(Rat)␀» 

Then, from a raku coder POV a valid mental model is that this is a divide by zero "non-failure" aka a ZBR that is made by creating a Inf (ie. by coercing a ZBR to an IEEE P754 Inf in the FPU since Num s are doubles) and then coercing it back to a Rat which as a construct of Ints lives in the CPU.

And I can make the identical thing via an FPU overflow like this:

say 1.7976931348623157e309;  # OUTPUT: «Inf␀» 
say 1.7976931348623157e309 === <1/0>.Num; # OUTPUT: «True␀» 

My point is that when you have done a bunch of P754 coding then it is most natural to think of Inf (aka the result of a DBZ op) as a creature of the FPU. This is the design of raku Num and the cool thing is the "interop" between Num and Rat - but also a source of potential complexity around DBZ behaviours.

So, obviously this would work too:

say (1e0/0e0).Rat; # OUTPUT: «Attempt to divide 1 by zero using /␀»...

.oO Realises that raku still supports DBZ exceptions in the FPU. Probably have to monkey with the compiler directives to put FPU in the "don't raise exception" mode atm. Would be nice to have that control on the same footing as eg my $*RAT-OVERFLOW one day.

It took me a while to realise that this Failure is from an FPU exception so maybe the details field could mention that? At least if not for now, then maybe this is the kind of nuanced message that could be put in details in future.