On 05/07/2020 23:55, Rob Cliffe wrote:
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.

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:

I do not agree clamp should be restricted to numeric values. I would expect clamp to be agnostic to the specifics of floats/numbers and like sort expect it to work for any values as long as they compare (using a dunder). I think having something like min=-math.inf is hence right out in my mind. If I got this right, the implementation could be as simple as:

def clamp(value, *, min=None, max=None):
  if min is not None and value < min:
    return min
  if max is not None and max < value:
    return max
  return value


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().

On 06/07/2020 04:39, Chris Angelico wrote:
In terms of
reading poorly, this is far worse:

clamp(x, 10)

Does that ensure that it's no *greater* than 10 or no *less* than 10?
Since the args would be min before max, I would expect that this has a
lower bound and no upper bound, but there'll be people equally
confident that it should behave like range() and have an upper bound
with no lower bound (which would probably be more useful in a lot of
situations anyway).

So I also agree that the bounds should be given explicitly.


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.