The three cases force every code using ranges to deal with them. Apply this way if thinking many times for multiple concepts and you end up with a spaghetti of multiply nested if statements that's near impossible to analyze for correctness. Because now you have to read all code instead of just a tiny subset.
> What are these special open-ended items? How do you need to extend comparison to account for them?
The whole point of abstraction is to make those decisions once and isolate the complexity in one place instead of having it spread over N places in the code, forcing everybody to solve the same problems again and again.
Do ranges not support a limited set of operations through which the rest of the code can interact with them, instead of manipulating the endpoints directly?
I would think of the range itself as the abstraction, and then it matters less how it's implemented since any potential problems are local to the implementation and cheap-ish to fix.
Yes, this is correct. The range is the abstraction, and then you can choose how to represent it. Not much difference between the three range cases, and the single case with special endpoints, except that the three cases are more general, as no special values are needed.
Sure you can probably also do it, but this is not the way how the code was originally written. The original ranges present the bounds in their public API, and most code just operated on them.
Then I would say that's the problem. Whatever implementation you leak, the problem is the lack of implementation hiding, not that the implementation looks this way or that way.
I agree, your point still holds and if anything becomes stronger, because now we've identified a concept at a higher level of abstraction to replace endpoint operations!
Hard disagree here. Encapsulation is just one aspect of implementing the abstraction. And I'm not even convinced it is the best way here. Leaving the ranges as simple structures with two public fields, but introducing an "infinity" concept is another way to go. No encapsulation but still abstract (although one may argue this is still encapsulation but at a different level - applied to range bounds instead of whole ranges).
Encapsulation of ranges doesn't actually fully solve the problem, but just moves and isolates the complexity to the private implementation of the range concept (likely a class in OOP). E.g. you want to compute if two ranges overlap - you still have to deal with the complexity of 3 cases in each argument, so total 9 cases.
And hiding the bounds is likely going to be a lot more intrusive on the existing caller code (more refactoring).
> What are these special open-ended items? How do you need to extend comparison to account for them?
The whole point of abstraction is to make those decisions once and isolate the complexity in one place instead of having it spread over N places in the code, forcing everybody to solve the same problems again and again.