Sorry for the delay.
The concern is around clamping behavior vs doing something else across many of these APIs. I propose clamping behavior as being more intuitive and versatile than other alternatives, but there are definitely tradeoffs.
You brought up
insert(_:at:). What should the behavior be for something like
myCollection.insert(5, at: .first + 10) when
myCollection has fewer than 10 elements? Similarly, what should the behavior of
myCollection.replaceSubrange(..<(.first+10), with: [1,2,3]) be?
- Operation is a nop
- Operation traps
- Operation clamps
Option #1 is very surprising. If the developer called
remove, etc., they expect something to happen. Silently nop-ing is probably the least intuitive alternative.
Option #2 is surprising given that
OffsetBound is otherwise a higher level, non-trapping representation of abstract positions within a collection.
And that leave Option #3, where the element(s) will be inserted at the end of the collection, even if the collection is short.
This rationale seems to intuitively extend to new collection APIs for the future, such as
As for more dubious formulations such as a negative offset from the front of a collection, I think we should pursue some reasonable and consistent behavior for them. But, we shouldn’t significantly change our strategy to accommodate them at the cost of common, sane usage.
For ranges of
OffsetBound, there is not an obvious length to the range if the lower bound is relative to the front of the collection and the upper bound relative to the back. It depends on the collection to which it is applied. Clamping in the middle is consistent with the “hole in the middle” interpretation (e.g. that comparison uses). Similarly, there is no run-time or API notion of the distance between two
OffsetBounds (even if similarly anchored). As you point out, similarly-anchored offsets could visually imply such a length. However, consistent treatment with differently-anchored offsets means the length is contingent on the length of the collection to which it is applied.
Thinking about this more, I think there is a strong argument that a fully-disjoint range or offset supplied to e.g.
removeSubrange() should signal the error in some fashion. However, I feel that interior-clamping, that is clamping in the middle for opposing anchors or when only one bound dangles off the end still makes more sense.