
on Sun Nov 23 2008, "Dave Handley" <dave-AT-dah.me.uk> wrote:
Response inlined - thread snipped where we agree :)
David Abrahams wrote:
What's your problem with this thread? The problem is certainly getting a lot of attention.
My main issue with this thread is not the attention it received - it's the extent to which a discussion about a breaking change to a library has turned into a philosophical discussion. Really the discussion about what a range is should have taken place at review time for the library, now we should just be able to confirm or not that a breaking change has happened, and either fix it or not.
I think if you look back at the thread you'll see that you introduced the first philosophical statements.
Sure. The argument I was responding to included statements like,
"This implies to me that range is trying to look and feel like a container - not like an iterator."
and
"...if you think of a range as being more like an iterator than a container, but I would argue that it is more like a container."
Those (maybe unintentionally) are general statements about the range concept rather than a specific one about iterator_range.
This is all based on the following - the first line in the *current* docs for boost.range.
"A Range is a concept similar to the STL Container concept."
The same pre-amble goes on to talk about the differences between a range and a container; for example it says:
"In particular, a Range does not necessarily
* own the elements that can be accessed through it, * have copy semantics, "
That sounds familiar. In other words, a container has copy semantics and owns its elements.
It doesn't say that a range doesn't have a concept of emptiness,
But a range *does* have a concept of emptiness!
or that it has properties of an iterator. This is why I have been contending that ranges are more container like than they are iterator like. I'm not making this up - it's what the docs tell me.
I understand that you feel the docs gave you reason to think that, but IMO drawing that conclusion from the docs you cite is a stretch at best. But you don't want to talk about philosophy, so let's drop it.
I don't think containers in general have a single "defining feature". I agree that owning the data is a common feature of many containers, but so is the interface they have. A lot of containers have a superset of the interface we see on boost::iterator_range.
If you're going to argue about peoples' expectations for a category of types based on its similarity to some notion of "container," you at least have to make reference to some commonly-understood definition of the term. I chose the one in the standard, because that's what we all have access to.
Ok then - based on the contents of the docs here: http://www.boost.org/doc/libs/1_37_0/libs/range/doc/range.html what does that page mean? I'm basing the similarity on what the docs tell me to expect - are the docs wrong?
To the extent to which they imply that every default-constructed Range should be testable for emptiness, yes. But frankly, I don't see such an implication in that text except as a stretch. You have just as much reason to expect all Ranges to have begin() and end() members (they don't, in general) and Allocator template arguments (ditto), and insert() members, etc. And now we're back to philosophy. So, sorry.
It's unfortunate that the original implementation of iterator_range set up different expectations for you, but AFAICT the only thing that a Range has in common with a container is that it supplies a begin() and end() that delimit a sequence of elements.
And an empty() and a size()
Okay, fine, but with a different (free function) syntax.
empty() and size() on an iterator_range are both member functions - so I'm not sure what you mean here. Boost::iterator_range also provides front and back.
Yes, but now you've switched backfrom talking about the range concept to talking about a specific model of that concept.
Are you surprised?
Not insofar as you and I seem to have very different ideas about what constitutes a valid argument here.
That's the original topic of this thread, and the key point. Again - this is precisely why I stopped posting to boost development a long time back - a simple problem becomes a lengthy philosophical discussion.
If you want to stick with iterator_range I'm more than happy to. "Philosophy" in this conversation exists only because you made assertions about the Range concept that I can't accept.
I'm not talking about anything complex here. I've read the docs, I've looked at the code, I've used the code. Somewhere along the way we seem to now need exact definitions of iterator, container, range, and a whole host of other things in order to figure out how to resolve an undocumented breaking change.
Absolutely. I can't even begin to imagine how to resolve questions of whether the thing is broken in the first place without precise definitions. Sorry, I know I'm very literal-minded, but I don't know another way to thing these issues through.
The most obvious solution to this is 2 versions of the iterator_range class.
Yes, if Tom needs the old behavior, he should have it. The question is, how should he get it? He could write it himself, or Boost could provide it in any number of ways.
I think if I were the Range library maintainer, I would feel responsible enough for my initial design mistake to have made this transition a lot easier for my users, and I'd be bending over backwards to make up for the breakage. I would also be very reluctant to maintain the old design as anything more than a deprecated feature, though, because --- in my opinion --- it is fundamentally flawed.
So by that statement, you are saying that Tom's use case, which relied on the old behaviour, is fundamentally flawed?
No.
It is; however, what the library intends it to be.
Hm?
That assertion comes from the comment in the code that says iterator_range is container-like.
Apparently the "intentions of the library" are a bit like the shifting sands.
Why then do the docs still assert that a range is container-like? That's the 1.37 docs, not the 1.34 docs.
I can't account for the way the thing is documented. Do you really think that one of the two designs in question was inconsistent with Thorsten's intentions when he wrote it?
Incidentally, Tom cannot have both behaviors under the same name (including namespace) without either violating the ODR or introducing still more runtime overhead, thread safety problems, and other global state evil. If he is going to use both behaviors, I'd prefer he use the new behavior under the existing name and use a different name elsewhere. If that was indeed a viable option for him (though I expect it's not), the workaround would be easy: provide his own old_iterator_range
I realise that - which was why I believe 2 versions under different names is by far and away the best way to resolve this. From my perspective I don't much care what they are each called, but I do strongly believe (in the absence of a better solution) that 2 versions is the way forward.
Okay, but should they both be in Boost, now that the damage is done? I'm not sure that's the right answer either. And, by the way, I'm not saying it's the wrong answer; I'm saying I haven't been convinced in either direction. If Tom isn't prepared to accept using boost::old_iterator_range instead of boost::iterator_range, we have to break a bunch of other peoples' code, which seems worse to me. Then both groups of users will have been disrupted. If Tom *is* prepared to use boost::old_iterator_range, IMO it may as well be tom::iterator_range: we don't have to have this component in Boost at all, except as an example along with the change notes that tell you how to write a component with the old behavior if you need it. -- Dave Abrahams BoostPro Computing http://www.boostpro.com