I really like this proposal, and think that it does have a place in Swift 4. 
Two quick notes in light of the discussion: first, I think it should be called 
clamped, not clamp; second, I think it should only take ClosedRange. More on 
those later, but first I'll respond to the six questions raised by Xiaodi.

> 1. Is it truly a frequent operation?
I think so. I've certainly wished for it on an occasion or two. I settle for 
min(upper, max(lower, value)).

> 2. Is the helper more readable? Is the composed equivalent obvious at a 
> glance?
Definitely (or I imagine it will be once we get the details figured out). There 
are two equivalent forms of the min-max version, the other being max(lower, 
min(upper, value)), not to mention the commutativity of the arguments 
themselves. I am under the impression that Swift is not a big fan of having 
multiple equivalent ways to do the same thing — that was part of the reason ++ 
was nixed. value.clamp(to: closedRange) is clear and is not interchangeable 
with any one thing in the language.

> 3. Does the helper have the flexibility to cover all common cases?
I see three cases: value < lower, lower <= value <= upper, and upper < value. 
All are covered.

> 4. Is there a correctness trap with the composed equivalent? Is there a 
> correctness trap with the helper?
I don't think so, if we limit to ClosedRange.

> 5. Is there a performance trap with the composed equivalent? Or with the 
> helper?
I don't know, is there a significant cost associated to constructing a 
ClosedRange solely for the purpose of using its bounds? I would imagine not, 
but someone who knows more about Swift can answer.

> 6. Does the helper actually encourage misuse?
I don't see how, if we limit its argument to ClosedRange.


Going back to my earlier points — I think that to keep things in line with 
Swift's naming conventions, this function should be called clamped, as it 
returns a modified version of the calling object. Alternatively, we could 
follow the standard set by other numeric types and provide the non-mutating 
clamped and the mutating clamp, like multiplied/multiply for Double.

Finally, I don't think it makes mathematical sense to clamp to a non-closed 
range. Refer back to the original definition proposed, `min(upperBound, 
max(lowerBound, value))`. ClosedRange was proposed as a convenience for 
providing those bounds. This makes sense because a ClosedRange contains its 
bounds. Since (mathematical) non-closed ranges don't contain their bounds, it 
doesn't make sense to use a non-closed range to provide those bounds.

Also, the above notwithstanding, I have a hard time figuring out when you would 
actually want to constrain a number to be strictly less than an upper bound, 
violating Question 1 above. If this behavior were really desired, better to be 
explicit and subtract the appropriate delta — 1 for Int, Double.epsilon (or 
whatever it's called) for Double. I definitely foresee a correctness trap with 
the non-closed Range.

Another reason not to allow half-open ranges is because of their asymmetry. 
Half open ranges are only open at their upper end, so you would have the 
ability to open-clamp from above but not from below. Seems arbitrary (see 
Question 3).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to