digitalmars.D - Re: "the last change" for ranges
- MLT <none anone.com> May 20 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> May 20 2009
- Jason House <jason.james.house gmail.com> May 20 2009
- MLT <none anon.com> May 20 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> May 20 2009
- dsimcha <dsimcha yahoo.com> May 20 2009
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> May 20 2009
- "Nick Sabalausky" <a a.a> May 20 2009
- Robert Fraser <fraserofthenight gmail.com> May 20 2009
- dsimcha <dsimcha yahoo.com> May 20 2009
- Brad Roberts <braddr puremagic.com> May 20 2009
Andrei Alexandrescu Wrote:In wake of a few discussion I've witnessed, I'm thinking of a last change for ranges. (In fact there's one more, but that's minor.) The problem is that input ranges and forward ranges have the same syntactic interface, but different semantic interfaces. Consider the problem of finding the first two identical adjacent items in a range: R adjacentFind(R)(R r) { if (r.empty) return r; R last = r; r.popFront; for (; !r.empty && last.front != r.front; last.popFront, r.popFront) { } return r; } This will work properly on lists and vectors, but horrendously on files and sockets. This is because input ranges can't be saved for later use: incrementing r also increments popFront and essentially forces both to look at the same current value.
I think that ifR last = r;
r.popFront;
If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
May 20 2009
MLT wrote:I think that ifR last = r;
r.popFront;
type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.) If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
That's the case for all ranges except input ranges. Consider: FileByCharacter { private FILE* _f; private dchar _last; bool empty() { return _last == 0xffff; } void popFront() { _last = fgetc(_f); } dchar front() { return _last; } } Consider what happens when you copy this range around. Andrei
May 20 2009
Andrei Alexandrescu Wrote:MLT wrote:I think that ifR last = r;
r.popFront;
type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.) If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
That's the case for all ranges except input ranges. Consider: FileByCharacter { private FILE* _f; private dchar _last; bool empty() { return _last == 0xffff; } void popFront() { _last = fgetc(_f); } dchar front() { return _last; } } Consider what happens when you copy this range around. Andrei
You didn't declare FileByCharacter as either a struct or a class? Did I plant a seed? :)
May 20 2009
Andrei Alexandrescu Wrote:MLT wrote:I think that ifR last = r;
r.popFront;
type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.) If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
That's the case for all ranges except input ranges. Consider: FileByCharacter { private FILE* _f; private dchar _last; bool empty() { return _last == 0xffff; } void popFront() { _last = fgetc(_f); } dchar front() { return _last; } } Consider what happens when you copy this range around. Andrei
I thought more of something like: FileByCharacter { private FILE* _f; private dchar[] _buf; bool empty() { return _buf[0] == 0xffff; } void popFront() { _buf = _buf[1..$] ; if( _buf.length < 1 ) _buf ~= fgetc(_f); } dchar front() { return _buf[0]; } } The idea is that you continue to expand an array. Another copy of the range will continue to step over the same array. This doesn't really work, because the second copy doesn't really know how much the first copy already read. But that should be fixable.... the problem is in the line if( _buf.length < 1 ) _buf ~= fgetc(_f); which should only trigger if _buf reached the end of the read part, not the end of the current copy of _buf. I'm also not sure if D's GC will handle dropping the part of the array that no one looks at. One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
May 20 2009
MLT wrote:One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
That's a great abstraction, but we can't afford to impose that to everybody. There must be an abstraction for a one-pass go through an arbitrarily long stream. Anyhow, I decided to change ranges as follows: a) Input: bool empty(); ref T popNext(); b) Output: void putNext(T); c) Forward: bool empty(); ref T front(); void popFront(); The function ref T popNext() is a nonmember that accepts any forward range and uses front() and popFront(). Of course, a forward range is welcome to implement popFront as a member if it so wishes. The function putNext() overwrites front() and then calls popFront. d) Bidirectional: bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); popNext, putNext apply as for forward ranges. e) Random bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); ref T opIndex(uint n); void opIndexAssign(T, uint n); popNext, putNext apply as for forward ranges. We need to fix the opIndexAssign mess. Andrei
May 20 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleMLT wrote:One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
everybody. There must be an abstraction for a one-pass go through an arbitrarily long stream. Anyhow, I decided to change ranges as follows: a) Input: bool empty(); ref T popNext(); b) Output: void putNext(T); c) Forward: bool empty(); ref T front(); void popFront(); The function ref T popNext() is a nonmember that accepts any forward range and uses front() and popFront(). Of course, a forward range is welcome to implement popFront as a member if it so wishes. The function putNext() overwrites front() and then calls popFront. d) Bidirectional: bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); popNext, putNext apply as for forward ranges. e) Random bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); ref T opIndex(uint n); void opIndexAssign(T, uint n); popNext, putNext apply as for forward ranges. We need to fix the opIndexAssign mess. Andrei
Please, please, please PLEASE, PRETTY PLEASE FOR THE LOVE OF GOD ALMIGHTY tell me you're not serious!!! Isn't changing the interface such that forward ranges are no longer effectively a subtype of input ranges a bit drastic? Or do you have some magic up your sleeve that, given any forward range, will automatically call popFront, and then front, when popNext is called, using extension function hacks or something? The whole beauty of ranges is that they provide one standard interface to program to if all you need is a lowest common denominator level of functionality. Frankly, if you destroy this, I think that just enforcing forward vs. input ranges purely by convention would be the lesser of two evils.
May 20 2009
dsimcha wrote:Please, please, please PLEASE, PRETTY PLEASE FOR THE LOVE OF GOD ALMIGHTY tell me you're not serious!!! Isn't changing the interface such that forward ranges are no longer effectively a subtype of input ranges a bit drastic? Or do you have some magic up your sleeve that, given any forward range, will automatically call popFront, and then front, when popNext is called, using extension function hacks or something?
Consider: struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015The whole beauty of ranges is that they provide one standard interface to program to if all you need is a lowest common denominator level of functionality. Frankly, if you destroy this, I think that just enforcing forward vs. input ranges purely by convention would be the lesser of two evils.
You and I see eye to eye. Andrei
May 20 2009
"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gv2hj8$k1n$1 digitalmars.com...dsimcha wrote: Consider: struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015
I thought that was only supposed to work for arrays. Has that changed? If so, what's the new rule?
May 20 2009
Nick Sabalausky wrote:"Andrei Alexandrescu" <SeeWebsiteForEmail erdani.org> wrote in message news:gv2hj8$k1n$1 digitalmars.com...dsimcha wrote: Consider: struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015
I thought that was only supposed to work for arrays. Has that changed? If so, what's the new rule?
If a.fun(args) doesn't find fun, rewrite to fun(a, args). Andrei
May 20 2009
Andrei Alexandrescu wrote:struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015
Yes. Oh yes. YES!!!
May 20 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleMLT wrote:One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
everybody. There must be an abstraction for a one-pass go through an arbitrarily long stream. Anyhow, I decided to change ranges as follows: a) Input: bool empty(); ref T popNext(); b) Output: void putNext(T); c) Forward: bool empty(); ref T front(); void popFront(); The function ref T popNext() is a nonmember that accepts any forward range and uses front() and popFront(). Of course, a forward range is welcome to implement popFront as a member if it so wishes. The function putNext() overwrites front() and then calls popFront. d) Bidirectional: bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); popNext, putNext apply as for forward ranges. e) Random bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); ref T opIndex(uint n); void opIndexAssign(T, uint n); popNext, putNext apply as for forward ranges. We need to fix the opIndexAssign mess. Andrei
(Bangs head against desk.) Sorry. Didn't see the part about the non-member popNext(), though this would require Walter to make extension methods work for structs, which should probably happen anyway.
May 20 2009
dsimcha wrote:== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleMLT wrote:One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
everybody. There must be an abstraction for a one-pass go through an arbitrarily long stream. Anyhow, I decided to change ranges as follows: a) Input: bool empty(); ref T popNext(); b) Output: void putNext(T); c) Forward: bool empty(); ref T front(); void popFront(); The function ref T popNext() is a nonmember that accepts any forward range and uses front() and popFront(). Of course, a forward range is welcome to implement popFront as a member if it so wishes. The function putNext() overwrites front() and then calls popFront. d) Bidirectional: bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); popNext, putNext apply as for forward ranges. e) Random bool empty(); ref T front(); void popFront(); ref T back(); void popBack(); ref T opIndex(uint n); void opIndexAssign(T, uint n); popNext, putNext apply as for forward ranges. We need to fix the opIndexAssign mess. Andrei
(Bangs head against desk.) Sorry. Didn't see the part about the non-member popNext(), though this would require Walter to make extension methods work for structs, which should probably happen anyway.
I should learn to never post before reading all messages... Andrei
May 20 2009
Andrei Alexandrescu wrote:dsimcha wrote:Please, please, please PLEASE, PRETTY PLEASE FOR THE LOVE OF GOD ALMIGHTY tell me you're not serious!!! Isn't changing the interface such that forward ranges are no longer effectively a subtype of input ranges a bit drastic? Or do you have some magic up your sleeve that, given any forward range, will automatically call popFront, and then front, when popNext is called, using extension function hacks or something?
Consider: struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015 Andrei
It was in the talk at the first conference as a 'todo for v2', but it's still on the todo list. :) Later, Brad
May 20 2009