I think the reason it's weird is that we might intuitively think of the "enforce a lower bound" function as taking two named arguments (lowerBound and inputValue) and the order of those two arguments mattering.
But of course, it turns out that the order of the arguments doesn't matter: applying a lowerBound of 5 to an inputValue of 100 turns out to be the exact same thing as applying a lowerBound of 100 to an inputValue of 5.
We know that the order of arguments doesn't matter for the Math.max function, so I think that's where the moment of incredulity comes from.
I think your "at most" language is pretty expressive. You could do that as an alias for `min` and `max`
I think `at_most(at_least(num, lower_bound), upper_bound)` is much easier to understand instantly than `min(max(...))`.
I'm tempted to make these aliases myself in some of my development actually. I find a pretty big conceptual difference between "I want to find the minimum point in this data", and "I want to restrict the range of this number" that giving them different names will probably help the readability of my code.
(Of course, for `min(max(...))` I usually write a `clamp()` function to hide that for me, but someones I want to only clamp in one direction)
> (Of course, for `min(max(...))` I usually write a `clamp()` function to hide that for me, but someones I want to only clamp in one direction)
You could make clamp work in only one direction too. clamp(number, None, upper_bound) or the idiomatic equivalent in your language of choice seems pretty readable.
But of course, it turns out that the order of the arguments doesn't matter: applying a lowerBound of 5 to an inputValue of 100 turns out to be the exact same thing as applying a lowerBound of 100 to an inputValue of 5.
We know that the order of arguments doesn't matter for the Math.max function, so I think that's where the moment of incredulity comes from.