Hi, is there an update on this proposal? Is this something that needs to be completed before ABI lock-in or can it wait?
@Douglas_Gregor commented on the PR at (Returned for Revisions) [SE-0177] Revise clamping proposal by Nirma · Pull Request #723 · apple/swift-evolution · GitHub saying it was put on ice pending further discussion but I haven't found anything further.
It would be great to see this included – It's something that I seem to encounter on a near daily basis.
I've attempted my own version with the suggestions of @xwu. A deviation from the original proposal is to constrain the ClosedRange
and PartialRangeUpTo
variants to FloatingPoint
types. clamping to something like (0..<0) would be undefined anyhow, but FloatingPoint
at least allows us to return .nan in this case – so I think this might make more sense than Strideable
which doesn't seem to have the concept of a minimum stride as far as I can tell.
// Comparable extension
extension Comparable {
func clamped<T: ClampableRangeExpression>(to range: T) -> Self where T.Bound == Self {
return range.clamping(self)
}
}
// RangeExpression extension
protocol ClampableRangeExpression: RangeExpression {
func clamping(_ value: Bound) -> Bound
}
// Conformance for various Range Types
extension PartialRangeFrom: ClampableRangeExpression {
func clamping(_ value: Bound) -> Bound {
return contains(value) ? value : lowerBound
}
}
extension PartialRangeThrough: ClampableRangeExpression {
func clamping(_ value: Bound) -> Bound {
return contains(value) ? value : upperBound
}
}
extension ClosedRange: ClampableRangeExpression {
func clamping(_ value: Bound) -> Bound {
return (lowerBound...).clamping((...upperBound).clamping(value))
}
}
// Constrained conformance for relevant Range Types
extension PartialRangeUpTo: ClampableRangeExpression where Bound: FloatingPoint {
func clamping(_ value: Bound) -> Bound {
return (...upperBound.nextDown).clamping(value)
}
}
extension Range: ClampableRangeExpression where Bound: FloatingPoint {
func clamping(_ value: Bound) -> Bound {
guard lowerBound != upperBound else { return .nan }
return (lowerBound...).clamping((..<upperBound).clamping(value))
}
}
The alternative is the simple:
public func clamped<T>(_ lowerBound: T, _ upperBound: T, _ value: T) -> T where T : Comparable {
return min(upperBound, max(lowerBound, value))
}
Which seems like it would fit alongside the existing min
and max
implementations quite neatly to me, but I understand there has been some discussion which discourages this direction.
Either way, it would be good to get a solution of some sort.