
On Sun, Jul 5, 2020 at 5:49 PM Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Jul 05, 2020 at 11:58:58AM -0700, Bruce Leban wrote:
But using a NAN is not an unreasonable operation.
I didn't say that it was. A NaN is the *result* of an operation that cannot produce a number. I didn't intend the word "unreasonable" to mean anything more than that, just as you don't have to be crazy to use irrational numbers.
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.
Supported, yes. Recommended, no. IEEE-754 specifies that the *minimum *operation propagates NaNs while the *alternate **minimumNumber *operation drops NaNs. [image: image.png]
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.
Convenience of implementation is not (to me) a compelling argument for what the semantics of an operation are.
Duck-typing for the win!
Always agree with this.
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
This is unfortunate and does not follow the IEEE-754 guidelines as noted above.
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
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
I don't usually write code that *uses *NaNs directly. If poisoning 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.
What you're suggesting is that everyone who doesn't normally use NaNs should be forced to think about it and test for it, just in case, because you're going to hide the errors. I would suspect there is much more code out there that does not check for NaNs then does. And most people who use the clamp function (or min and max today) won't check for it. What I'm saying is that those who care about NaNs should be the ones to test for it. 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?
This has nothing to do with rights. That's a fallacious argument. Defining a stdlib function one way or another does not damage your rights. And I'm not forcing you to check twice. What happens inside the clamp function is not code you are writing. If this is a performance argument, then many would say you shouldn't be writing Python. 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.)
I agree with this. It is unfortunate that min and max have this behavior which I would consider a bug. I don't want clamp to have buggy behavior either. --- Bruce