[swift-evolution] Add a `clamp` function to Algorithm.swift

Xiaodi Wu xiaodi.wu at gmail.com
Fri Mar 10 19:42:03 CST 2017


On Fri, Mar 10, 2017 at 6:29 PM, James Froggatt <james.froggatt at me.com>
wrote:

>
> On 11 Mar 2017, at 00:21, James Froggatt <james.froggatt at me.com> wrote:
>
>
> On 11 Mar 2017, at 00:05, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> Some days ago, Ben Cohen laid out the criteria for helper functions in the
> Standard Library. Here's some of his very enlightening text and the six
> criteria:
>
> The operation needs to carry its weight. Even once we have ABI stability,
>> so the size of the std lib becomes less of a concern as it could ship as
>> part of the OS, we still need to keep helper method growth under control.
>> APIs bristling with methods like an over-decorated Xmas tree are bad for
>> usability. As mentioned in the String manifesto, String+Foundation
>> currently has over 200 methods/properties. Helpers are no good if you can’t
>> find them to use them.
>>
>
>
>> 1. Is it truly a frequent operation?
>> 2. Is the helper more readable? Is the composed equivalent obvious at a
>> glance?
>> 3. Does the helper have the flexibility to cover all common cases?
>> 4. Is there a correctness trap with the composed equivalent? Is there a
>> correctness trap with the helper?
>> 5. Is there a performance trap with the composed equivalent? Or with the
>> helper?
>> 6. Does the helper actually encourage misuse?
>
>
> The reasons I'm opposed to adding `clamp` are as follows:
>
> It is trivially composed from `min` and `max`, with no correctness traps.
>
> As the discussion above shows, there are correctness traps when you have a
> `clamp` operation that takes open ranges, whereas the composed form using
> `min` and `max` does not suffer from the same issue.
>
> It encourages misuse, because Dave's desired use case (for indices) works
> *only* for arrays and falls down for collections. This is similar to the
> problem which motivates removal of `enumerated()` as discussed in other
> threads. In this case, it is not guaranteed that a collection with indices
> `0..<10` has an index 9.
>
>
> You make a good point, but then how exactly did the range-clamping
> function make it into the standard library in the first place? I can't
> think of frequent reason to want to clamp a range to within another range
> putting my mind to it, yet a clamp function on the Bound type has uses with
> arrays and offers a clear improvement to readability. Then there's the
> (potential) correctness trap of mixing up min and max, which I find leads
> me to need to double-check the logic after typing.
>
> Seeing those criteria just makes it all the more frustrating that the
> range-clamping version is the one to have made the cut.
>
>
> Rereading, you're point is that the range-clamping version does solve a
> correctness trap (and that the Bound version does not?). Could you explain
> how you reached to this conclusion?
>

I'm not making that point. I'm just pointing out that these are the current
criteria for expanding the standard library.

If, looking back, you feel that the existing `clamped(to:)` doesn't fit the
criteria, then you can propose its removal. However, there are also
standards for changing existing APIs in Swift 4. The first one is that "the
existing syntax/API being changed must be actively harmful"--which is a bar
that `clamped(to:)` doesn't meet, IMO.

On Fri, Mar 10, 2017 at 4:48 PM, James Froggatt via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> This topic caught my attention. I support the idea, I'm currently using
>> an extension for this.
>>
>> >>Should “16.clamped(to: 0..<10)” produce 9 or 10?
>>
>> >9
>>
>> Sounds good.
>>
>> >>What about “16.clamped(to: 0..<0)”, which is an empty range?
>>
>> >For `Int`? Crash (which, until about 5 minutes ago, is what I thought
>> would happen if you tried to create a range that’s empty like that). For
>> types that support it, I’d say NaN or something like “nil”/“empty” is the
>> most appropriate return value
>>
>> Nasty but reasonable. I'd support it returning nil in this case, this
>> would provide a warning that the result may not be a valid number, as well
>> as providing this elegant range validation:
>>
>> `if let index = candidate.clamped(to: array.indices) { … }`
>>
>> (or a possible alternative spelling:)
>>
>> `if let index = array.indices.clamp(candidate) { … }`
>>
>> >>Does “16.0.clamped(to: 0..<10)” yield 10.0 or the next-smaller
>> representable Double?
>>
>> >Next-smaller, IMHO. It’s not exactly semantically correct, but AFAIK
>> that’s as correct as Float/Double can be.
>>
>> One could argue the most ‘correct’ value here is the closest
>> representation of the theoretical value, `10.0 - (1 / ∞)`, which should
>> clearly round to 10. However, this also rounds the result back out of the
>> range, meaning it's unsuitable as a result. Both possibilities are, for
>> lack of a better word, ugly.
>>
>> >Mostly though I’d really like to be able to clamp to array indices,
>> which are pretty much always written as a `Range`, rather than a
>> `ClosedRange`. We could write the function for `Range` to only be generic
>> over `Comparable&Integer`, if the floating point corner cases are too much.
>>
>> > - Dave Sweeris
>>
>> My conclusion also. I'd like to see this added to the standard library,
>> if it's in scope for Swift 4.
>>
>> Sidenote: I can't help but think index validation would be better solved
>> in many cases by an optional-returning array subscript (`array[ifPresent:
>> index]`), but I've seen this solution turned down several times due to the
>> lack of discoverability (read: lack of Xcode autocompletion, which I
>> originally thought was a bug until it stayed that way for ~3 years). I'd
>> also like to see this feature get added in some form, eventually.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170310/8f12ea70/attachment.html>


More information about the swift-evolution mailing list