On Sun, Jul 05, 2020 at 11:58:58AM -0700, Bruce Leban wrote:

> > People who want NAN poisoning can opt-in by doing a check for NANs
> > themselves, either in a wrapper function, or by testing the bounds
> > *once* ahead of time and then just calling the stdlib `clamp` once they
> > know they aren't NANs.
> 
> 
> Imagine making the same statement about exceptions:
> 
>  Exceptions being raised should be opt-in.
> 
> 
> That sounds crazy but it's not. Before exceptions were commonplace, there
> were three possibilities when unreasonable operations were performed:

But using a NAN is not an unreasonable operation. There is a perfectly 
sensible interpretaion available for using NANs as bounds, and it is 
one which is supported by the IEEE-754 recommended treatment 
of minimum and maximum: missing values.

That same behaviour falls out naturally from a very simple, non- 
contrived and efficient implementation of clamp that relies only on the 
value supporting less than. (The bounds also have to support equality.) 
It doesn't even have to be numeric! So long as the value supports less 
than, you can clamp it. Whether that is meaningful or not depends on the 
value, but the feature is there for those who can make use of it. 
Duck-typing for the win!


>    - return some arbitrary or random value (not a NaN because they hadn't
>    been invented);

IEEE-754 goes back to the 1980s, before exceptions were commonplace.

So returning a NAN was certainly an option. On the Apple Mac, converting 
a string to a float would return a NAN if it was a non-numeric value 
like "abc". So did operations like arcsin(2).


>    - and also set an "error flag" that could be checked by the caller to
>    determine if something had gone wrong (but frequently was not);
>    - terminate the program.
> 
> Things are much better now. Few argue that it was better before.

There are many people who consider exceptions to be a terrible mistake, 
including the creators of Go. So your analogy is not as strong as you 
think.


> Think of
> NaN as the value equivalent of an exception. NaN poisoning is the
> equivalent of the fact that any function that doesn't catch an exception
> passes it through.

Right. That's an excellent analogy. If exceptions were uncatchable, they 
would always be fatal and they would be just like the bad old days where 
any error would always terminate the program.

But they aren't uncatchable, and there are situations where NANs don't 
represent a fatal error that can only propagate through your calculation 
poisoning the results.

Here are some examples:

    py> 1 < NAN
    False

    py> min(max(1, NAN), NAN)
    1

    py> 1**NAN
    1.0

The first two are especially relevant for clamp.


> I don't usually write code that *uses *NaNs directly. If
> I want a distinguished non-numeric value, I use None or some other
> sentinel. If a NaN is produced by my code it indicates a bug. NaN poisoning
> increases the chance that a NaN generated somewhere won't be hidden by
> later code that manipulates that value. Why would I want to suppress that?

Then don't. I'm happy for you to test for it. Write a wrapper function 
that checks the bounds, and you are no worse off than if it was builtin.

In many use-cases, you won't even need a wrapper function, because the 
bounds won't be changing, or will change only rarely. So you only need 
test them once, not on every call to clamp. Win!

I respect your right to check the bounds for NANs. How about you respect 
my right to not to, and don't force me to do it *twice* to get the 
behaviour I want?


> Just as an exception can be suppressed by explicit code, Nan poisoning can
> be suppressed by explicit checks. 

What you want is for people who want to test for NANs to have the clamp 
function do it for them, and people who don't want to check for NANs to 
have to do it twice, once in their wrapper function, and once in the 
clamp function.

Your attitude here is literally:

"Oh, you don't want to check for NANs? Then I'll make you do it twice as 
much as everyone else!"


> So let me rewrite your second and third
> paragraphs above (recall Option 1 which you favor is ignore NaNs, and
> Option 2 is NaN poisoning):
> 
> Let's say the stdlib uses Option 2. The function doesn't need to do any
> explicit checks for NANs,

But that's not true, it does need to check for NANs explicitly, because 
order comparisons `x < NAN` don't return NANs.

There may be some clever arrangement of arguments for min and max that 
will return a NAN, but that's depending on accidental behaviour. You 
can't rely on it.

(Having the result of min and max depend on the order of arguments is 
not a feature, and relying on that accidental behaviour is not safe.)



-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/XNENXWYGRRVJSK2MNAN5VOY42RMIITJA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to