Re: Add builtin function for min(max())

On the topic of NaNs: I think in the face of ambiguity, we should refuse the temptation to guess and raise ValueError when the values given are not comparable to the point of clearly determining whether `value` is or isn't within `minimum` and `maximum` or which of the two bounds it exceeds. It is the programmer's responsibility to ensure they supply arguments that make sense, and non-numbers should be considered exceptional behaviour: as such, the programmer that anticipates those kinds of values should handle them exceptionally. About the name: clamp seems to be the most favoured name and the discussion convinced me it's the one that should make it to the eventual proposal. About the stdlib/builtin status: `min(max(x, min_), max_)` is a common enough idiom for a simple enough function that I think it is definitely beneficial and in no way dangerous to have, and that any status other than builtin would incur too much overhead for something so small. I risk claiming that most calls to max() and min() occur in modules that do not `import math` (or at the very least, they occur often enough without `import math`), and I believe clamp(), being very similar in scope to those, should benefit from similar status. I don't think clamp is a common enough identifier in non-specific fields to disqualify its implementation as a builtin by itself. By contrast, terms like `min` and `max` are extremely common in pretty much all contexts, and yet we've all survived just fine by typing `min_` and `max_` or even shadowing the names altogether (which I prefer not to do, but is definitely an option). FInally, I'd like to lit another fire: given that `min()` and `max()` accept an arbitrary amount of arguments, and that the signature that seems to be preferred for the hypothetical clamp at the moment sees `value` as the first positional argument, should the function then accept iterables as values for `minimum` and `maximum`? Something along the lines of: clamp(value: Number, minimum: Union[Number, Iterable[Number]], maximum: Union[Number, Iterable[Number]])

On Sun, Jul 5, 2020 at 2:36 PM Federico Salerno <salernof11@gmail.com> wrote:
+1
Has anyone suggested a .clamp method on the various numerical classes? Since this is a convenience that's easy to do without or implement yourself, I don't think it's important that it's super general and can instantly be used with anything comparable.

On Sun, Jul 5, 2020 at 6:15 AM MRAB <python@mrabarnett.plus.com> wrote:
clamp(value: Number, minimum: Union[Number, Iterable[Number]], maximum: Union[Number, Iterable[Number]])
Ss ( IsA Zzz What would that return? What if the iterable were two different lengths? If anything, I would accept an iterable for value, and return an iterator. Min() with an iterable is essential a reduce. Clamp() with an iterable would be
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Sorry, that got sent too soon. On Sun, Jul 5, 2020 at 1:59 PM Christopher Barker <pythonchb@gmail.com> wrote:
Though then there is the question of what to return for a scalar value: an iterator with one value or a scalar? So maybe these kinds of operations should be left to numpy. -CHB

On 05/07/2020 10:09, Federico Salerno wrote:
+1 to all of the above (I don't feel strongly about the name). All seems very sensible.
-1. As others have said, this is over-egging it. I think None should be allowed as a missing bound. I don't think the new function should be restricted to numbers. There may be uses for strings, or for user-built classes; why restrict it unnecessarily? If it quacks like supporting __lt__ and __gt__, it's a duck. Rob Cliffe

On 05/07/2020 23:55, Rob Cliffe wrote:
I'm not opposed to this, although the semantics of calling it with strings may not be intuitive (does it compare the length? Alphabetic order? What order is respected for CJK or symbols?); on the other hand, we already have such behaviour on min() and max() so adding it to clamp() should reasonably be expected to follow the same logic. On that note: On 06/07/2020 04:21, Henk-Jaap Wagenaar wrote:
+1 I think it's important to have None as sentinel value for "unlimited bound" as it is non-numeric (and non-anything-else) and thus automatically supports any comparable type. Otherwise, only numbers would benefit from the feature and anything else would need some kind of custom instance just so that anything compared True for any < and > operation. Practicality should always win when it comes to tools as general as clamp().
I agree as well. I know most here (at least out of those who gave an opinion) don't seem to like it but an advantage of the originally proposed signature: clamp(min, val, max) Is that it would automatically force explicit bounds and place min and max in completely unambiguous order respective to each other. I know clamp(val, min, max) is familiar because it reflects the structure of min(max(val, min), max) but the clarity falls apart when this is reduced to one function call, so it may be worth considering alternatives; it has also been argued that consistency in appearance between min(max()) and clamp() should not be expected.

On 06/07/2020 10:44, Federico Salerno wrote:
The behaviour for strings should be as per the existing string comparisons. Anything else would be surprising. The same goes for objects of any other class that supports the relational operators. Although using it for e.g. sets would be unusual and come labelled with "consenting adults, use at own risk". Rob Cliffe

On Sun, Jul 5, 2020 at 2:36 PM Federico Salerno <salernof11@gmail.com> wrote:
+1
Has anyone suggested a .clamp method on the various numerical classes? Since this is a convenience that's easy to do without or implement yourself, I don't think it's important that it's super general and can instantly be used with anything comparable.

On Sun, Jul 5, 2020 at 6:15 AM MRAB <python@mrabarnett.plus.com> wrote:
clamp(value: Number, minimum: Union[Number, Iterable[Number]], maximum: Union[Number, Iterable[Number]])
Ss ( IsA Zzz What would that return? What if the iterable were two different lengths? If anything, I would accept an iterable for value, and return an iterator. Min() with an iterable is essential a reduce. Clamp() with an iterable would be
-- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

Sorry, that got sent too soon. On Sun, Jul 5, 2020 at 1:59 PM Christopher Barker <pythonchb@gmail.com> wrote:
Though then there is the question of what to return for a scalar value: an iterator with one value or a scalar? So maybe these kinds of operations should be left to numpy. -CHB

On 05/07/2020 10:09, Federico Salerno wrote:
+1 to all of the above (I don't feel strongly about the name). All seems very sensible.
-1. As others have said, this is over-egging it. I think None should be allowed as a missing bound. I don't think the new function should be restricted to numbers. There may be uses for strings, or for user-built classes; why restrict it unnecessarily? If it quacks like supporting __lt__ and __gt__, it's a duck. Rob Cliffe

On 05/07/2020 23:55, Rob Cliffe wrote:
I'm not opposed to this, although the semantics of calling it with strings may not be intuitive (does it compare the length? Alphabetic order? What order is respected for CJK or symbols?); on the other hand, we already have such behaviour on min() and max() so adding it to clamp() should reasonably be expected to follow the same logic. On that note: On 06/07/2020 04:21, Henk-Jaap Wagenaar wrote:
+1 I think it's important to have None as sentinel value for "unlimited bound" as it is non-numeric (and non-anything-else) and thus automatically supports any comparable type. Otherwise, only numbers would benefit from the feature and anything else would need some kind of custom instance just so that anything compared True for any < and > operation. Practicality should always win when it comes to tools as general as clamp().
I agree as well. I know most here (at least out of those who gave an opinion) don't seem to like it but an advantage of the originally proposed signature: clamp(min, val, max) Is that it would automatically force explicit bounds and place min and max in completely unambiguous order respective to each other. I know clamp(val, min, max) is familiar because it reflects the structure of min(max(val, min), max) but the clarity falls apart when this is reduced to one function call, so it may be worth considering alternatives; it has also been argued that consistency in appearance between min(max()) and clamp() should not be expected.

On 06/07/2020 10:44, Federico Salerno wrote:
The behaviour for strings should be as per the existing string comparisons. Anything else would be surprising. The same goes for objects of any other class that supports the relational operators. Although using it for e.g. sets would be unusual and come labelled with "consenting adults, use at own risk". Rob Cliffe
participants (5)
-
Alex Hall
-
Christopher Barker
-
Federico Salerno
-
MRAB
-
Rob Cliffe