<div dir="ltr">Looks great, thanks!<br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jun 26, 2019 at 7:04 PM Juan Vuletich <<a href="mailto:juan@jvuletich.org">juan@jvuletich.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hello,<br>
<br>
I had a chance to work on a few things with Andres Valloud, here is his <br>
summary of what we just posted on Github.<br>
<br>
First, we turned Complex into a loadable package once more.  Usually the <br>
disadvantage is that loading such packages results in code overrides.  <br>
For instance, since the message sqrt will have to change behavior (so <br>
that -1 sqrt returns a complex number, rather than fail outright), one <br>
would expect that loading Complex would have to modify the base system <br>
to introduce the new feature.  This time, however, we engineered how <br>
Complex interacts with the rest of the system so that loading the <br>
Complex package does not result in any base system code modifications.  <br>
Of course, loading Complex causes -1 sqrt to evaluate to 0 + 1i, as before.<br>
<br>
The key to accomplishing this was to introduce a new exception, <br>
NegativePowerError, which by default comes in without a defaultAction <br>
method.  When Complex loads, it simply provides defaultAction so that <br>
said exceptions are handled by invoking complex number arithmetic.  We <br>
hope this mechanism illustrates one way of making different code <br>
packages coexist more harmoniously.  Please include Complex as a <br>
requirement to your packages as appropriate.<br>
<br>
Second, and along the same lines, we also found that the arithmetic <br>
error exceptions looked very similar and yet their implementation was <br>
very different.  Further, there were some discrepancies in how they were <br>
used and handled (some would do things like ^ZeroDivide signal..., and <br>
some others simply ZeroDivide signal...).  In addition, for floating <br>
point numbers, some of the results were not following the IEEE spec.<br>
<br>
As an aside, it seems trivial, but even the following four operations <br>
require *substantial* spec lawyering to get right:<br>
<br>
0.0 - 0.0<br>
0.0 - -0.0<br>
-0.0 - 0.0<br>
-0.0 - -0.0<br>
<br>
Looks trivial --- it isn't.  And then you consider +, /, and *.  So, we <br>
reorganized these exceptions under a general class called <br>
ArithmeticMessageError.  This new abstract exception has three instance <br>
variables that will look familiar immediately: receiver, message, and <br>
arguments.  These are provided by the location where the exception <br>
occurs, which in turn allows e.g. defaultAction to resume the <br>
computation as required without having to either a) guess what went <br>
wrong, or b) needing a multitude of different exceptions depending on <br>
what actually happened.  Currently, ArithmeticMessageError has two <br>
subclasses for situations that are special enough to deserve their own <br>
class: division by zero, and raising a negative number to powers.<br>
<br>
In the "rationals" (i.e. Integer and Fraction), things like 1/0 are <br>
undefined. But in floating point land, dividing aFloat by zero can be <br>
one of three things (!): +INF, -INF, and NaN.  No, this was not our <br>
idea, but we care that things work according to the manual so that our <br>
system enables as many folks as possible.  Since floating point requires <br>
special handling, there is a ZeroDivide exception to do so.  Something <br>
similar happens with NegativePowerError.  In the "rationals", things <br>
like -8 raisedTo: 1/3 make sense.  That's because, generally,<br>
<br>
a^(p/q) = (a^p)^1/q<br>
<br>
Note the sign of the result, when it is defined, depends on the sign of <br>
a and (essentially) the "parity" of p/q.  Again, this was not our idea, <br>
but we gather the world mostly works this way :).<br>
<br>
With floating point numbers, however, it's not obvious what a^b is when <br>
a and b are floating point numbers.  Suppose a is negative, and b is 1.0 <br>
/ 3.0.  Looking at that floating point 1/3, does that mean that if the <br>
last bit of the mantissa is zero, that the result does not exist?  And <br>
that if the last bit of the mantissa is 1, then it's ok to take <br>
something *like* the cubic root of a?  What does that even mean when b <br>
is the result of who knows how many operations, each of which is <br>
presumably on the order of one ulp off?  Thus, said powers fail, and the <br>
result is NaN for floating point numbers.<br>
<br>
If you are curious, consider the fantastic behavior of the following two <br>
examples.  Both fail gracefully, as they should.<br>
<br>
-32 raisedTo: (1/5.0) asTrueFraction<br>
-32 raisedTo: (1/5.0) successor asTrueFraction<br>
<br>
Note that if Complex is loaded, however, a^b will happily work for <br>
floating point numbers and produce complex numbers, as expected.  For <br>
instance, take G_6.  Since -8 is 8 * w_3, then one of the possible cubic <br>
roots is 2 * w_2 (and so is 2 * w_5).<br>
<br>
If this behavior does not work for you, look for the relevant <br>
defaultAction method, and also take a look at floatErrorValue.  We tried <br>
to make these moldable and easy to change so that people can configure <br>
the system to their liking, while requiring minimal modifications.<br>
<br>
Third, all these improvements to exceptions brought up the problem that <br>
SUnit was catching errors by handling Error.  This is not ideal because, <br>
as just exemplified above, any exception can provide a defaultAction <br>
method that recovers its occurrence.  But handling Error like this:<br>
<br>
[3 / 0] on: Error do: [:ex | ex return: 'everybody knows this is bad']<br>
<br>
prevents Error from running its defaultAction, and so a recoverable <br>
error always becomes an unrecoverable error.  Instead, the above code <br>
should say this:<br>
<br>
[3 / 0] on: UnhandledError do: [:ex | ex return: 'evidently this error <br>
was unexpected']<br>
<br>
Consequently, TestResult exError should answer UnhandledError, rather <br>
than Error.  We applied this change.  Please consider how you handle <br>
exceptions with these observations in mind to maximize flexibility and <br>
robustness.<br>
<br>
Andres thinking the above should be at least mostly correct, Juan <br>
approving without being too worried.<br>
<br>
</blockquote></div>