digitalmars.D - getNext
- Andrei Alexandrescu (18/18) Jul 12 2010 I think I figured out a comfortable and all-encompassing means to define...
- Jonathan M Davis (20/46) Jul 12 2010 What happens to empty, front, and popFront then? Is this a case where so...
- Andrei Alexandrescu (29/75) Jul 12 2010 An input range could either define the troika empty/front/popFront or
- Rory McGuire (4/94) Jul 12 2010 :) I like it.
- bearophile (67/70) Jul 13 2010 Time ago I have filed bug http://d.puremagic.com/issues/show_bug.cgi?id=...
- Shin Fujishiro (8/25) Jul 13 2010 How about a TLS variable?
- Shin Fujishiro (3/10) Jul 13 2010 Yeah, I just missed it. TLS is not usable. I had spoken carelessly...
- Andrei Alexandrescu (10/34) Jul 13 2010 There's the classic problem of reusing the same temporary. Consider:
- Shin Fujishiro (4/14) Jul 13 2010 To my shame, I missed it!
- Michel Fortin (9/17) Jul 13 2010 At this point what you want is to use a foreach loop. In fact, if
- Steven Schveighoffer (28/40) Jul 13 2010 struct InputForeach(R) if(isInputRangeThatUsesGetNext!R)
- FeepingCreature (4/13) Jul 13 2010 One way to make this slightly easier would be allowing auto in loop bodi...
- =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= (8/21) Jul 23 2010 =
- David Piepgrass (5/12) Jul 09 2012 I don't know if this proposal went anywhere since 2010, but it
- David Piepgrass (25/35) Jul 09 2012 Amazing. My post unleashed four pages of comments and not one of
- Jesse Phillips (3/8) Jul 12 2010 I'm with Jonathan on this. I don't really see much of a benefit. popFron...
- Ellery Newcomer (12/30) Jul 12 2010 let's see here
- Steven Schveighoffer (13/30) Jul 13 2010 Yes, yes, yes!
- Andrei Alexandrescu (5/40) Jul 13 2010 I need to discuss this with Walter, he mentioned that it wouldn't be
- Mehrdad (20/32) Jul 09 2012 Oh dear. I can see this is getting ugly, pretty darn fast...
- Andrei Alexandrescu (3/9) Jul 09 2012 What if you want to return a reference so the user can change it?
- Mehrdad (8/23) Jul 09 2012 Huh? You don't... it's an input iterator, not an output iterator!
- Mehrdad (2/4) Jul 09 2012 er... s/iterator/range/g
- Mehrdad (2/5) Jul 09 2012 Not to mention, what's the use case for it?
- Andrei Alexandrescu (4/20) Jul 09 2012 The idea of an input range is that it works seamlessly with the more
- Mehrdad (6/11) Jul 09 2012 You mean 'foreach'? That should be using opApply if it wants to
- Andrei Alexandrescu (6/17) Jul 09 2012 I think it's about the notion of "input range" that is confusing, a
- Mehrdad (2/7) Jul 09 2012 Aren't output ranges single-pass too?
- Andrei Alexandrescu (4/12) Jul 09 2012 They don't have much of a notion of "pass" because the only primitive of...
- Mehrdad (12/29) Jul 09 2012 Sorry? I don't know what you mean, but "single-pass" makes
- Mehrdad (6/9) Jul 09 2012 Er, let me rephrase that:
- Andrei Alexandrescu (5/8) Jul 09 2012 That is the case right now. The point is, with your design you need to
- Mehrdad (31/44) Jul 09 2012 If that's the case, I'd hate to tell you this, but _unless_
- Andrei Alexandrescu (6/24) Jul 09 2012 As I mentioned, "input range" is a misnomer. Think "one-pass range". The...
- Mehrdad (21/32) Jul 09 2012 Sure, we all agree that a multi-pass range is also a one-pass
- Andrei Alexandrescu (12/26) Jul 09 2012 An output range only needs put(). In that sense it's an endless bag in
- Mehrdad (17/30) Jul 09 2012 If by "simplification" you mean "fewer methods to call", then yes.
- Andrei Alexandrescu (26/29) Jul 09 2012 You may want to just spell it clearly.
- Mehrdad (6/37) Jul 09 2012 that's not what I meant, but I think another solution is better
- Roman D. Boiko (5/10) Jul 09 2012 Could you make a detailed proposal? I still completely don't
- Jonathan M Davis (15/28) Jul 09 2012 What we really need is a solid article on ranges on the site (maybe even...
- Roman D. Boiko (5/5) Jul 09 2012 By the way, this thread is quite old.
- Mehrdad (13/20) Jul 09 2012 Well, I'm not even sure if the proposal is necessary yet, since
- Andrei Alexandrescu (3/10) Jul 09 2012 Instead of what? There is not transform() function in std.algorithm.
- Timon Gehr (4/24) Jul 09 2012 His post was related to this one, burried in another branch of the
- Mehrdad (5/8) Jul 09 2012 I was talking about this:
- Mehrdad (30/32) Jul 09 2012 If that's the case, I'd hate to tell you this, but _unless_
- Timon Gehr (3/14) Jul 09 2012 Consider the possibility that this electrical engineer might be a
- Mehrdad (9/11) Jul 09 2012 Yes, so if you expect him to be, then you should ask him "are you
- Timon Gehr (6/18) Jul 09 2012 The range is an input range of references. It could just as
- Mehrdad (7/11) Jul 09 2012 Yes... but when did I ever impose anything on the /elements/? I
- Timon Gehr (3/12) Jul 09 2012 Unlikely.
- Mehrdad (2/12) Jul 09 2012 Huh? Isn't that Andrei's post?
- Timon Gehr (2/18) Jul 09 2012
- Steven Schveighoffer (11/22) Jul 16 2012 import std.range;
- Mehrdad (2/2) Jul 09 2012 Perhaps if you could show an actual example of what you expect to
- jerro (10/12) Jul 09 2012 It is useful to be able to write an algorithm that both reads
- Mehrdad (21/33) Jul 09 2012 Wow, thanks a bunch. That makes it a LOT more clearer than
- Jonathan M Davis (11/41) Jul 09 2012 Except that how output ranges are written to and an input range with
- Mehrdad (20/31) Jul 09 2012 Oh!! So they're _exactly_ emulating C++ here, with
- Timon Gehr (15/45) Jul 09 2012 struct BST(K){
- Timon Gehr (3/54) Jul 09 2012 (but BSTs do not actually support a range that implements transform.
- Mehrdad (6/7) Jul 09 2012 A good comparison:
- Roman D. Boiko (6/12) Jul 09 2012 template isOutputRange(R,E)
- Michel Fortin (8/18) Jul 13 2010 This can't be @safe. getNext would need to take a pointer out of the
- Shin Fujishiro (19/42) Jul 14 2010 I gave it a try, and it fairly simplified range implementation.
- monarch_dodra (15/27) Jul 09 2012 This feels quite similar to "consumeFront"? As a matter of fact,
- monarch_dodra (4/8) Jul 09 2012 Re-reading the documentation of "map", I realize this is a bad
- Steven Schveighoffer (6/8) Jul 16 2012 I see you still defending this concept from 2010, does it mean we may
- Andrei Alexandrescu (12/21) Jul 16 2012 I'm not defending it, I recall last time I discussed it I mentioned its
- Steven Schveighoffer (11/34) Jul 16 2012 Yes, that is a true drawback. I suppose a nullable type would help here...
I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? Andrei
Jul 12 2010
On Monday 12 July 2010 20:48:05 Andrei Alexandrescu wrote:I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? AndreiWhat happens to empty, front, and popFront then? Is this a case where something must define either empty, front, and popFront or getNext to be an input range? Or is this something else? Personally, I find empty, front, and popFront quite useful and simple for anything that I've done, and I'd find getNext to be a lot more verbose. getNext may be great if you're using empty, front, and popFront pretty much simultaneously, but if you don't want to use all of them for whatever you're doing, then getNext is overkill. So, essentially, I suppose the issue is that I don't see what you intend to do to front, popFront, and empty if you add getNext into the mix. I do not want to see front, popFront, or empty go away. Having getNext as an additional function to make it easier to iterate over a range and do something with each element as you iterate wouldn't hurt my feelings any, but I definitely don't want to lose popFront, empty, or front. As for simplification, it strikes me as more complicated in every case except where you are iterating over a range and processing each element as you iterate. Granted, that's a common use case, but there are plenty of other cases, where if you were forced to use getNext instead of having popFront, empty, and front, that would be a major problem. - Jonathan M Davis
Jul 12 2010
On 07/12/2010 11:21 PM, Jonathan M Davis wrote:On Monday 12 July 2010 20:48:05 Andrei Alexandrescu wrote:An input range could either define the troika empty/front/popFront or getNext. In the former case, getNext detects the presence of the troika and uses it transparently. That's great because client code can simply use getNext throughout.I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns&item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? AndreiWhat happens to empty, front, and popFront then? Is this a case where something must define either empty, front, and popFront or getNext to be an input range? Or is this something else? Personally, I find empty, front, and popFront quite useful and simple for anything that I've done, and I'd find getNext to be a lot more verbose. getNext may be great if you're using empty, front, and popFront pretty much simultaneously, but if you don't want to use all of them for whatever you're doing, then getNext is overkill.So, essentially, I suppose the issue is that I don't see what you intend to do to front, popFront, and empty if you add getNext into the mix.There is no aggravation brought to the current definitions.I do not want to see front, popFront, or empty go away. Having getNext as an additional function to make it easier to iterate over a range and do something with each element as you iterate wouldn't hurt my feelings any, but I definitely don't want to lose popFront, empty, or front.My feelings too. The troika is here to stay, and definitely necessary for any range richer than an input range.As for simplification, it strikes me as more complicated in every case except where you are iterating over a range and processing each element as you iterate. Granted, that's a common use case, but there are plenty of other cases, where if you were forced to use getNext instead of having popFront, empty, and front, that would be a major problem.Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it: T meh; T * neh; while ((neh = getNext(r, meh))) { ... process *neh ... } I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) { ... } With this, allocating a dummy buffer on caller's stack is automated, so client code can just write: for (T * p; (p = getNext(r)); ) { ... process *p ... } I feel dirty. Andrei
Jul 12 2010
On Tue, 13 Jul 2010 06:38:55 +0200, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 07/12/2010 11:21 PM, Jonathan M Davis wrote::) I like it. But then looking at the alloca man page... yikesOn Monday 12 July 2010 20:48:05 Andrei Alexandrescu wrote:An input range could either define the troika empty/front/popFront or getNext. In the former case, getNext detects the presence of the troika and uses it transparently. That's great because client code can simply use getNext throughout.I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns&item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? AndreiWhat happens to empty, front, and popFront then? Is this a case where something must define either empty, front, and popFront or getNext to be an input range? Or is this something else? Personally, I find empty, front, and popFront quite useful and simple for anything that I've done, and I'd find getNext to be a lot more verbose. getNext may be great if you're using empty, front, and popFront pretty much simultaneously, but if you don't want to use all of them for whatever you're doing, then getNext is overkill.So, essentially, I suppose the issue is that I don't see what you intend to do to front, popFront, and empty if you add getNext into the mix.There is no aggravation brought to the current definitions.I do not want to see front, popFront, or empty go away. Having getNext as an additional function to make it easier to iterate over a range and do something with each element as you iterate wouldn't hurt my feelings any, but I definitely don't want to lose popFront, empty, or front.My feelings too. The troika is here to stay, and definitely necessary for any range richer than an input range.As for simplification, it strikes me as more complicated in every case except where you are iterating over a range and processing each element as you iterate. Granted, that's a common use case, but there are plenty of other cases, where if you were forced to use getNext instead of having popFront, empty, and front, that would be a major problem.Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it: T meh; T * neh; while ((neh = getNext(r, meh))) { ... process *neh ... } I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) { ... } With this, allocating a dummy buffer on caller's stack is automated, so client code can just write: for (T * p; (p = getNext(r)); ) { ... process *p ... } I feel dirty. Andrei
Jul 12 2010
Andrei Alexandrescu:T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) {Time ago I have filed bug http://d.puremagic.com/issues/show_bug.cgi?id=3822 on alloca(), but I think this code is not hit by it. I have recently written some C99 code and I have appreciated its Variable Length Arrays both in syntax, safety and performance gain. Compared to alloca() the VLAs have some advantages: - Their syntax is shorter and nicer, and it's natural for a C programmer; - no imports needed; - the semantics is more clear, because they define a new variable, so the size of their scope is the same as the in all other variables, while alloca() seems to have two possible different implementations; - VLAs are more typesafe, there is no need to use a cast. While the cast needed by alloca() may forbid it in SafeD code. - VLAs don't need sizeof(T), you just specify a type. The result is that the usage of alloca() feels dirty in both C and D, but Variable Length Arrays (VLA) of C99 don't feel dirty at all. On the other hand VLAs (and alloca) can produce a stack overflow, they are not so commonly useful (as alloca), and they can become essentially a third kind of arrays for D (this is not good). In D I'd like something like alloca() that needs no casts and is able to find the size by itself, avoiding the bug prone usage of T.sizeof. A way to do it is to use the same syntax used by C99 and allow a variable in the definition of a stack array: auto foo(int n) { int[n] arr; return arr; } The main difference is that in D when you return arr it gets copied by value. Currently this code works: int foo(int n) { return 3 * n + 1; } auto bar() { immutable int n = 5; int[foo(n)] arr; return arr; } void main() {} because dmd runs foo() at compile time, so arr is allocated on the stack with a statically known size. If VLAs get introduced in D, then in this case the compiler has to do what it currently it doesn't do: to run a function at compile-time if possible (and create a fixed length array) and run it at runtime if that's impossible (and create a VLA). To avoid that in some cases you can use something like: int[StaticValue!(n)] arr; Where StaticValue is a template that makes sure n is a value always known at compile-time. But this is a little messy, and I don't like it too much. Keep in mind that currently this gives an error: int[] bar() { int[2] arr; return arr; } void main() {} You have to use the auto return type or: int[2] bar() { int[2] arr; return arr; } void main() {} With VLAs you are forced to use auto, but it can't work anyway at the return point. So I think the normal C99 syntax for VLAs is not good for D2. On the other hand alloca() semantics and syntax are bad. So D alloca() can be replaced by something better like: T* ptr = StackAlloc!T(n); Or: T* ptr = Alloca!T(n); Or: T[] arr = VLA!T(n); But this created an array that looks like a dynamic array, but its memory is on the stack so it must not escape the function. So it can be a bug-prone, similar to this that currently compiles: int[] foo() { int[1] stackArray; int[] dynArray = stackArray[0 .. 1]; return dynArray; } void main() {} See enhancement request http://d.puremagic.com/issues/show_bug.cgi?id=4451 So with the syntax T[] arr=VLA!T(n); the compiler has to disallow the return of arr and its slices. Whith those three syntaxes your signature becomes: T* getNext(R, E)(ref R range, ref E store = *StackAlloc!E(1)) { T* getNext(R, E)(ref R range, ref E store = *Alloca!E(1)) { T* getNext(R, E)(ref R range, ref E store = *(VLA!E(1).ptr)) { Bye, bearophile
Jul 13 2010
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) { ... } With this, allocating a dummy buffer on caller's stack is automated, so client code can just write: for (T * p; (p = getNext(r)); ) { ... process *p ... } I feel dirty.How about a TLS variable? template temporary(T) { static T temporary; } E* getNext(R, E)(ref R range, ref E store = temporary!E); Shin
Jul 13 2010
Shin Fujishiro <rsinfu gmail.com> wrote:How about a TLS variable? template temporary(T) { static T temporary; } E* getNext(R, E)(ref R range, ref E store = temporary!E);Yeah, I just missed it. TLS is not usable. I had spoken carelessly... Shin
Jul 13 2010
On 07/13/2010 05:09 AM, Shin Fujishiro wrote:Andrei Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:There's the classic problem of reusing the same temporary. Consider: Range r1, r2; ElementType!Range * p1, p2; while ((p1 = getNext(r1)) && (p2 = getNext(r2))) { ... oops ... } You need one temporary for each static occurrence of getNext. AndreiI've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) { ... } With this, allocating a dummy buffer on caller's stack is automated, so client code can just write: for (T * p; (p = getNext(r)); ) { ... process *p ... } I feel dirty.How about a TLS variable? template temporary(T) { static T temporary; } E* getNext(R, E)(ref R range, ref E store = temporary!E);
Jul 13 2010
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:There's the classic problem of reusing the same temporary. Consider: Range r1, r2; ElementType!Range * p1, p2; while ((p1 = getNext(r1)) && (p2 = getNext(r2))) { ... oops ... } You need one temporary for each static occurrence of getNext.To my shame, I missed it! Anyways, I like getNext(). LockingTextReader definitely needs it. Shin
Jul 13 2010
On 2010-07-13 00:38:55 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it: T meh; T * neh; while ((neh = getNext(r, meh))) { ... process *neh ... }At this point what you want is to use a foreach loop. In fact, if foreach could be made to work with getNext (and it should), you'd rarely need to write that boilerplate code yourself. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 13 2010
On Tue, 13 Jul 2010 08:54:07 -0400, Michel Fortin <michel.fortin michelf.com> wrote:On 2010-07-13 00:38:55 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:struct InputForeach(R) if(isInputRangeThatUsesGetNext!R) { private R* range; this(ref R range) {this.range = ⦥} int opApply(scope int delegate(ref ElementType!R) dg) { ElementType!R buf; ElementType!R *e; int result = 0; while(!result && (e = getNext(*range, buf))) { result = dg(*e); } return result; } } InputForeach!R inputForeach(R)(ref R r) if(isInputRangeThatUsesGetNext!R) { return InputForeach!R(r); } foreach(e; inputForeach(r)) { ... } opApply to the rescue :) Of course, native support would be better, unless the compiler decides to start inlining opApply. -SteveYah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it: T meh; T * neh; while ((neh = getNext(r, meh))) { ... process *neh ... }At this point what you want is to use a foreach loop. In fact, if foreach could be made to work with getNext (and it should), you'd rarely need to write that boilerplate code yourself.
Jul 13 2010
On 13.07.2010 06:38, Andrei Alexandrescu wrote:Yah, truth be told getNext won't win a prize for brevity. You need to define both a variable and a pointer to use it: T meh; T * neh; while ((neh = getNext(r, meh))) { ... process *neh ... }One way to make this slightly easier would be allowing auto in loop bodies, same as it works in if - ie. T meh; while (auto neh = getNext(r, meh)) { }
Jul 13 2010
Dnia 13-07-2010 o 06:38:55 Andrei Alexandrescu = <SeeWebsiteForEmail erdani.org> napisa=B3(a):I've just had an idea that is so dark and devious, I was almost afraid==to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store =3D *(cast(E*) alloca(E.sizeof)) { ... } With this, allocating a dummy buffer on caller's stack is automated, =so =client code can just write: for (T * p; (p =3D getNext(r)); ) { ... process *p ... } I feel dirty.What's wrong with foreach(e; r) that you want to play dirty? The compile= r = is here to help. Tomek
Jul 23 2010
I've just had an idea that is so dark and devious, I was almost afraid to try it. But it works like a charm. Consider: T * getNext(R, E)(ref R range, ref E store = *(cast(E*) alloca(E.sizeof)) { ... }I don't know if this proposal went anywhere since 2010, but it occurs to me that there is a hidden danger here. alloca will allocate a sequence of separate temporaries. If the collection is large, the stack will overflow, and the client might not have a clue what happened.
Jul 09 2012
On Monday, 9 July 2012 at 07:53:41 UTC, David Piepgrass wrote:I don't know if this proposal went anywhere since 2010, but it occurs to me that there is a hidden danger here. alloca will allocate a sequence of separate temporaries. If the collection is large, the stack will overflow, and the client might not have a clue what happened.Amazing. My post unleashed four pages of comments and not one of them responded to my post :O I think Mehrdad is right that an in/out range should have its own name to distinguish it from an input range, but that doesn't necessarily mean that the same interface can't be used for both. I imagine a couple of advantages of:T tmp; for(T* front = r.getNext(ref tmp)) // do something with frontinstead of:for(; !r.empty; r.popFront()) // do something with r.front- If the range uses late-binding, getNext() is faster because you're only calling one function instead of 3. When I program in interface calls to get each item. Late binding, of course, is necessary across DLL boundaries and can help avoid code bloat. - If an input-only range has to unpack its elements (e.g. bit array => bool, or anything compressed), the range doesn't need to unpack repeatedly every time 'front' is accessed, nor does it need to reserve memory inside itself for a scratch area (you don't want scratch areas in every range if your app keeps track of thousands of ranges; plus, ranges tend to get passed by value, right?). That said, it may be unreasonable for the compiler to support the necessary escape analysis (impossible in case you're importing .di files)... and maybe the existing empty/popFront/front is too well established to reconsider? (I am not familiar with the status quo).
Jul 09 2012
Andrei Alexandrescu Wrote:getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? AndreiI'm with Jonathan on this. I don't really see much of a benefit. popFront, empty, front are very easy to define and simple to use. Java uses getNext for its iterators. Though it calls it 'next' and throws an exception when trying to call without any elements. This leads it to also provide a hasNext function.
Jul 12 2010
On 07/12/2010 10:48 PM, Andrei Alexandrescu wrote:I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null.let's see here Range AhmAStream; ubyte[] buf = new ubyte[4]; ubyte[]* ruf = AhmAStream.getNext(buf); assert(ruf && ruf.length == 4); int i = * (cast(int*) ruf.ptr) buf = new ubyte[i]; ruf = AhmAStream.getNext(buf); assert(ruf && ruf.length == i); Something like this could work within this interface, couldn't it? Think you might be on to somethinggetNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification? Andrei
Jul 12 2010
On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification?Yes, yes, yes! A question though -- whenever a pointer occurs, we always cringe, especially in safeD. will getNext be unsafe? BTW, I like the alloca thingy, that's really cool. One thing I just thought of, getNext should be split into two functions, the one you have, and: ElementType!R *getNext(R)(ref R range) To avoid having to supply the item or use alloca when the range is going to give you back a pointer to its internals anyways (tested with a template constraint). -Steve
Jul 13 2010
On 07/13/2010 07:39 AM, Steven Schveighoffer wrote:On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I need to discuss this with Walter, he mentioned that it wouldn't be difficult to allow certain uses of pointers in SafeD.I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification?Yes, yes, yes! A question though -- whenever a pointer occurs, we always cringe, especially in safeD. will getNext be unsafe?BTW, I like the alloca thingy, that's really cool. One thing I just thought of, getNext should be split into two functions, the one you have, and: ElementType!R *getNext(R)(ref R range) To avoid having to supply the item or use alloca when the range is going to give you back a pointer to its internals anyways (tested with a template constraint).Yup, good point. Andrei
Jul 13 2010
On Tuesday, 13 July 2010 at 12:39:19 UTC, Steven Schveighoffer wrote:On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Oh dear. I can see this is getting ugly, pretty darn fast... If you need to /pass/ TWO parameters by _reference_ AND return a pointer to a /templated/ method _just_ to get 1 element out of a range/array, the design is horribly wrong. Imagine all the sorts of problems newcomers would have with learning this. Not to mention the troubles /everyone/ would have with having this templated method a class. (How do you override it?) Really, doing something simple should be simple. I don't have any brilliant ideas, but one guess would be nullable types... if you can return a nullable value, your interface for an interface could just be: T? next(); and that's it... it would return null if nothing is left. (The compiler can just set the null flag and ignore initializing the rest of the struct if the result is null.) Anyway, whatever we do... _please_ don't make it something like that suggestion above >_< thanksWe've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: <snip>Yes, yes, yes! -Steve
Jul 09 2012
On 7/9/12 5:14 AM, Mehrdad wrote:Really, doing something simple should be simple. I don't have any brilliant ideas, but one guess would be nullable types... if you can return a nullable value, your interface for an interface could just be: T? next(); and that's it... it would return null if nothing is left.What if you want to return a reference so the user can change it? Andrei
Jul 09 2012
On Monday, 9 July 2012 at 14:03:17 UTC, Andrei Alexandrescu wrote:On 7/9/12 5:14 AM, Mehrdad wrote:Huh? You don't... it's an input iterator, not an output iterator! What would you be changing, exactly? D's return-by-reference doesn't work the way you expect anyway, so it's kinda pointless worrying about it.... property ref int foo() { } property void foo(int v) { } foo = 2; // huh?Really, doing something simple should be simple. I don't have any brilliant ideas, but one guess would be nullable types... if you can return a nullable value, your interface for an interface could just be: T? next(); and that's it... it would return null if nothing is left.What if you want to return a reference so the user can change it? Andrei
Jul 09 2012
On Monday, 9 July 2012 at 14:07:26 UTC, Mehrdad wrote:Huh? You don't... it's an input iterator, not an output iterator!er... s/iterator/range/g
Jul 09 2012
On Monday, 9 July 2012 at 14:07:26 UTC, Mehrdad wrote:Huh? You don't... it's an input iterator, not an output iterator! What would you be changing, exactly?Not to mention, what's the use case for it?
Jul 09 2012
On 7/9/12 10:07 AM, Mehrdad wrote:On Monday, 9 July 2012 at 14:03:17 UTC, Andrei Alexandrescu wrote:The idea of an input range is that it works seamlessly with the more capable ranges. AndreiOn 7/9/12 5:14 AM, Mehrdad wrote:Huh? You don't... it's an input iterator, not an output iterator!Really, doing something simple should be simple. I don't have any brilliant ideas, but one guess would be nullable types... if you can return a nullable value, your interface for an interface could just be: T? next(); and that's it... it would return null if nothing is left.What if you want to return a reference so the user can change it? Andrei
Jul 09 2012
On Monday, 9 July 2012 at 14:21:33 UTC, Andrei Alexandrescu wrote:You mean 'foreach'? That should be using opApply if it wants to take outputs by ref, not input range capabilities. Or is there some other constructor you're referring to? Because I don't see any overlap in the constructs between input and output ranges.Huh? You don't... it's an input iterator, not an output iterator!The idea of an input range is that it works seamlessly with the more capable ranges. Andrei
Jul 09 2012
On 7/9/12 10:31 AM, Mehrdad wrote:On Monday, 9 July 2012 at 14:21:33 UTC, Andrei Alexandrescu wrote:I think it's about the notion of "input range" that is confusing, a better name would be "single-pass range". One should be perfectly capable of assigning to elements of an input range. A built-in slice is an input range. AndreiYou mean 'foreach'? That should be using opApply if it wants to take outputs by ref, not input range capabilities. Or is there some other constructor you're referring to? Because I don't see any overlap in the constructs between input and output ranges.Huh? You don't... it's an input iterator, not an output iterator!The idea of an input range is that it works seamlessly with the more capable ranges. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 14:48:05 UTC, Andrei Alexandrescu wrote:I think it's about the notion of "input range" that is confusing, a better name would be "single-pass range". One should be perfectly capable of assigning to elements of an input range. A built-in slice is an input range. AndreiAren't output ranges single-pass too?
Jul 09 2012
On 7/9/12 10:52 AM, Mehrdad wrote:On Monday, 9 July 2012 at 14:48:05 UTC, Andrei Alexandrescu wrote:They don't have much of a notion of "pass" because the only primitive of output ranges is "put". AndreiI think it's about the notion of "input range" that is confusing, a better name would be "single-pass range". One should be perfectly capable of assigning to elements of an input range. A built-in slice is an input range. AndreiAren't output ranges single-pass too?
Jul 09 2012
On Monday, 9 July 2012 at 15:16:52 UTC, Andrei Alexandrescu wrote:On 7/9/12 10:52 AM, Mehrdad wrote:Sorry? I don't know what you mean, but "single-pass" makes perfect sense to me for output ranges. Think: CD burning. Anyway, what I'm saying is that if your code _only_ depends on the _input_ capabilities of a range, then it /cannot/ and _does not_ need the 'ref' capability. You only need 'ref' for two reasons, so far as I can see: - Writing - Optimization The latter point is moot here, and the former is _clearly_ not something an "input range" should have, because it's a notion of output...On Monday, 9 July 2012 at 14:48:05 UTC, Andrei Alexandrescu wrote:They don't have much of a notion of "pass" because the only primitive of output ranges is "put". AndreiI think it's about the notion of "input range" that is confusing, a better name would be "single-pass range". One should be perfectly capable of assigning to elements of an input range. A built-in slice is an input range. AndreiAren't output ranges single-pass too?
Jul 09 2012
On Monday, 9 July 2012 at 15:34:35 UTC, Mehrdad wrote:The latter point is moot here, and the former is _clearly_ not something an "input range" should have, because it's a notion of output...Er, let me rephrase that: The former is _clearly_ not a property of "input ranges". If something is both an input range and an output range, then sure, it can have that capability. But being able to write to something is _orthogonal_ to whether you can read from it.
Jul 09 2012
On 7/9/12 11:35 AM, Mehrdad wrote:If something is both an input range and an output range, then sure, it can have that capability. But being able to write to something is _orthogonal_ to whether you can read from it.That is the case right now. The point is, with your design you need to add something extra to allow writing to elements of a single-pass range. So your design does not simplify things as much as it might seem. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 15:48:35 UTC, Andrei Alexandrescu wrote:On 7/9/12 11:35 AM, Mehrdad wrote:If something is both an input range and an output range, then sure, it can have that capability. But being able to write to something is _orthogonal_ to whether you can read from it.That is the case right now. The point is, with your design you need to add something extra to allow writing to elements of a single-pass range. So your design does not simplify things as much as it might seem. AndreiWith your design you need to add something extra to allow writing to elements of a single-pass range.If that's the case, I'd hate to tell you this, but _unless_ you're planning on removing the notion of input/output ranges (and perhaps adding single-pass/multi-pass), you're doing it wrong. :-) That capability is simply /not needed/ when your /only/ contract is that something is an input range. Asking an input range, "hey, can I write to you?" is just... insulting! That's like asking an electrical engineer if he can plumb. Sure, it might come in handy if he can, but it's just plain silly to ask himi that. If you need someone who can do both, then ask for someone who's an EE _and_ a plumber. On the other hand, the concepts of single-pass and multi-pass are most certainly NOT orthogonal, for obvious reasons. So it DOES make sense to ask an output range, "hey, can I write to you multiple times?" So if you're expecting to be able to say void foo(R)(ref R range) if (isInputRange!R) { static if (hasLValueElements!R) // or whatever it was range.front = (ElementType!R).init; } then you're mixing up two completely unrelated/orthogonal concepts, which is silly. What you SHOULD be saying instead is: void foo(R)(ref R range) if (isInputRange!R) { static if (isOutput!R) // or whatever it was range.put((ElementType!R).init); }
Jul 09 2012
On 7/9/12 12:04 PM, Mehrdad wrote:On Monday, 9 July 2012 at 15:48:35 UTC, Andrei Alexandrescu wrote:Given the smart-aleck nature of the comment I'd say s/hate/love/.On 7/9/12 11:35 AM, Mehrdad wrote:If something is both an input range and an output range, then sure, it can have that capability. But being able to write to something is _orthogonal_ to whether you can read from it.That is the case right now. The point is, with your design you need to add something extra to allow writing to elements of a single-pass range. So your design does not simplify things as much as it might seem. AndreiWith your design you need to add something extra to allow writing to elements of a single-pass range.If that's the case, I'd hate to tell you this, but _unless_ you're planning on removing the notion of input/output ranges (and perhaps adding single-pass/multi-pass), you're doing it wrong. :-)That capability is simply /not needed/ when your /only/ contract is that something is an input range.As I mentioned, "input range" is a misnomer. Think "one-pass range". The range can be written or not, and a multi-pass range (including random-access range) is also a one-pass range. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 18:51:40 UTC, Andrei Alexandrescu wrote:lol, nice comment. +1If that's the case, I'd hate to tell you this, but _unless_ you're planning on removing the notion of input/output ranges (and perhaps adding single-pass/multi-pass), you're doing it wrong. :-)Given the smart-aleck nature of the comment I'd say s/hate/love/.Sure, we all agree that a multi-pass range is also a one-pass range. I don't have a problem with that. But if you only use the one-pass range aspect, that doesn't mean it has to use the same syntax as the multi-pass range aspect. The issue is, while you are /telling/ me that "input range" is a misnomer, that doesn't match what Phobos's is telling me. In Phobos, you've placed a very clear difference between "input ranges" and "output ranges", and /both/ of them are single-pass. It looks like it was deliberately designed that way... I don't think you made a mistake when separating input and output ranges like that (unless you do?). So unless you're planning on trashing all that and redesigning the entire thing from scratch (are you?) I don't understand how you can have two orthogonal bases for working with ranges like this. (Pardon the math lingo, but that's really what they are -- input/output vs random-access/multi-pass-sequential-access/single-pass-sequential-access are two different bases for looking at the issue. You can't really mix them up in the same module...)That capability is simply /not needed/ when your /only/ contract is that something is an input range.As I mentioned, "input range" is a misnomer. Think "one-pass range". The range can be written or not, and a multi-pass range (including random-access range) is also a one-pass range.
Jul 09 2012
On 7/9/12 3:03 PM, Mehrdad wrote:Sure, we all agree that a multi-pass range is also a one-pass range. I don't have a problem with that. But if you only use the one-pass range aspect, that doesn't mean it has to use the same syntax as the multi-pass range aspect.But that works against your goal of simplifying things.The issue is, while you are /telling/ me that "input range" is a misnomer, that doesn't match what Phobos's is telling me. In Phobos, you've placed a very clear difference between "input ranges" and "output ranges", and /both/ of them are single-pass.An output range only needs put(). In that sense it's an endless bag in which you get to put stuff without any control over e.g. navigation. A one-pass range may or may not accept assignment to .front. In particular, put() is written to detect and use that. And that's about it.It looks like it was deliberately designed that way... I don't think you made a mistake when separating input and output ranges like that (unless you do?).I sense a Jedi trick is tried unto me.So unless you're planning on trashing all that and redesigning the entire thing from scratch (are you?) I don't understand how you can have two orthogonal bases for working with ranges like this.I agree with your assessment that you don't have a good understanding of how D ranges work. Nevertheless, proposing an alternative design would be a great basis for discussion; isolated fragments of designs look attractive in isolation but often fail to offer the whole picture. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 19:22:39 UTC, Andrei Alexandrescu wrote:But that works against your goal of simplifying things.If by "simplification" you mean "fewer methods to call", then yes. If by "simplification" you mean "easier to understand", then no -- not here. Having a different (but still simple) syntax for a more capable range is far better than the getNext() proposal here.I sense a Jedi trick is tried unto me.lol wasn't intending to, but whatever.An output range only needs put(). In that sense it's an endless bag in which you get to put stuff without any control over e.g. navigation. A one-pass range may or may not accept assignment to .front. In particular, put() is written to detect and use that. And that's about it. I agree with your assessment that you don't have a good understanding of how D ranges work.I certainly don't have a good understanding of how they were _intended_ to work, is what I found out in this thread. The docs (or even the names, as you mentioned yourself) don't at all convey what you guys explained here, so if they were designed one way but intended to work another way, then /of course/ I don't have a good understanding of how they were intended to work!Nevertheless, proposing an alternative design would be a great basis for discussion; isolated fragments of designs look attractive in isolation but often fail to offer the whole picture.I agree. My alternative would be to abandon similar 'hasXYZ' stuff (which doesn't convey the picture and looks hacky), and instead formally define what those are, like I/O range. Sounds good/bad?
Jul 09 2012
On 7/9/12 3:30 PM, Mehrdad wrote:I agree. My alternative would be to abandon similar 'hasXYZ' stuff (which doesn't convey the picture and looks hacky), and instead formally define what those are, like I/O range. Sounds good/bad?You may want to just spell it clearly. So right now we have the notions: input range (well one-pass range) forward range bidirectional range random-access range output range coupled with the capability queries isInfinite hasAssignableElements hasLength ... The small set of ranges coupled with the capability queries reflect the orthogonal or near-orthogonal nature of such. As far as I understand the above, you propose the notions: input range input range with assignable elements infinite input range infinite input range with assignable elements forward range forward range with assignable elements ... That seems pretty onerous, but then I can't derive other meaning from your post. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 19:52:04 UTC, Andrei Alexandrescu wrote:On 7/9/12 3:30 PM, Mehrdad wrote:that's not what I meant, but I think another solution is better anyway: Why isn't transform taking in both an input and an output range in the first place? Of course they might be the same, but they don't have to be.I agree. My alternative would be to abandon similar 'hasXYZ' stuff (which doesn't convey the picture and looks hacky), and instead formally define what those are, like I/O range. Sounds good/bad?You may want to just spell it clearly. So right now we have the notions: input range (well one-pass range) forward range bidirectional range random-access range output range coupled with the capability queries isInfinite hasAssignableElements hasLength ... The small set of ranges coupled with the capability queries reflect the orthogonal or near-orthogonal nature of such. As far as I understand the above, you propose the notions: input range input range with assignable elements infinite input range infinite input range with assignable elements forward range forward range with assignable elements ... That seems pretty onerous, but then I can't derive other meaning from your post. Andrei
Jul 09 2012
On Monday, 9 July 2012 at 20:21:18 UTC, Mehrdad wrote:that's not what I meant, but I think another solution is better anyway: Why isn't transform taking in both an input and an output range in the first place? Of course they might be the same, but they don't have to be.Could you make a detailed proposal? I still completely don't understand neither the problem you're trying to solve, nor the solution you have in mind. I was reading all your posts in this thread very carefully.
Jul 09 2012
On Monday, July 09, 2012 22:25:57 Roman D. Boiko wrote:On Monday, 9 July 2012 at 20:21:18 UTC, Mehrdad wrote:What we really need is a solid article on ranges on the site (maybe even several, with each one going into more detail on specific stuff - e.g. issues with strings being ranges of dchar). Then it will be much easier for programmers to gain a solid understanding of ranges. The API documentation will never be good enough for that, because it's not the right place to go into that level of detail. I keep meaning to write up such an article (and even started one a while back), but I never get around to getting it done... In the meantime, there's alway's Ali's book: http://ddili.org/ders/d.en/ranges.html It may be that there are things that can and should be improved about how ranges currently work, but I think that it's clear that any proposal made to improve them needs to be made with a solid understanding of how they currently work. - Jonathan M Davisthat's not what I meant, but I think another solution is better anyway: Why isn't transform taking in both an input and an output range in the first place? Of course they might be the same, but they don't have to be.Could you make a detailed proposal? I still completely don't understand neither the problem you're trying to solve, nor the solution you have in mind. I was reading all your posts in this thread very carefully.
Jul 09 2012
By the way, this thread is quite old. getNext looks similar to what I wanted when first dealt with ranges, but now it looks too heavyweight. What happened to this proposal anyway? Was it deferred, discarded, or what?
Jul 09 2012
On Monday, 9 July 2012 at 20:25:58 UTC, Roman D. Boiko wrote:On Monday, 9 July 2012 at 20:21:18 UTC, Mehrdad wrote:Well, I'm not even sure if the proposal is necessary yet, since it would be solved another way -- I'm not sure if transform should be taking 1 range as input or 2. I mean like, why isn't it defined this way instead? void transform(alias f, RI, RO)(RI r, RO output) if(isInputRange!RI && isOutputRange!RO) { for(; !r.empty; r.popFront()) output.put(unaryFun!f(r.front)); } If that works, then I still think we don't need assignable front()s after all.Why isn't transform taking in both an input and an output range in the first place? Of course they might be the same, but they don't have to be.Could you make a detailed proposal? I still completely don't understand neither the problem you're trying to solve, nor the solution you have in mind.
Jul 09 2012
On 7/9/12 4:47 PM, Mehrdad wrote:I mean like, why isn't it defined this way instead? void transform(alias f, RI, RO)(RI r, RO output) if(isInputRange!RI && isOutputRange!RO) { for(; !r.empty; r.popFront()) output.put(unaryFun!f(r.front)); }Instead of what? There is not transform() function in std.algorithm. Andrei
Jul 09 2012
On 07/09/2012 11:04 PM, Andrei Alexandrescu wrote:On 7/9/12 4:47 PM, Mehrdad wrote:His post was related to this one, burried in another branch of the thread: On 07/09/2012 08:33 PM, jerro wrote:I mean like, why isn't it defined this way instead? void transform(alias f, RI, RO)(RI r, RO output) if(isInputRange!RI && isOutputRange!RO) { for(; !r.empty; r.popFront()) output.put(unaryFun!f(r.front)); }Instead of what? There is not transform() function in std.algorithm. AndreiIt is useful to be able to write an algorithm that both reads and writes range elements. There are plenty of use cases for that, but if you really need an example, here's a simple one: void transform(alias f, R)(R r) if(isInputRange!R && hasAssignableElements!R) { for(; !r.empty; r.popFront()) r.front = unaryFun!f(r.front); }
Jul 09 2012
On Monday, 9 July 2012 at 21:04:31 UTC, Andrei Alexandrescu wrote:Instead of what? There is not transform() function in std.algorithm. AndreiI was talking about this: http://forum.dlang.org/thread/i1gnlo$18g0$1 digitalmars.com?page=5#post-tdgxpwegpwaczardhvpx:40forum.dlang.org If you had a different use-case in mind for 'ref' front()'s, would you mind posting it?
Jul 09 2012
With your design you need to add something extra to allow writing to elements of a single-pass range.If that's the case, I'd hate to tell you this, but _unless_ you're planning on removing the notion of input/output ranges (and perhaps adding single-pass/multi-pass), you're doing it wrong. :-) That capability is simply /not needed/ when your /only/ contract is that something is an input range. Asking an input range, "hey, can I write to you?" is just... insulting! That's like asking an electrical engineer if he can plumb. Sure, it might come in handy if he can, but it's just plain silly to ask himi that. If you need someone who can do both, then ask for someone who's an EE _and_ a plumber. On the other hand, the concepts of single-pass and multi-pass are most certainly NOT orthogonal, for obvious reasons. So it DOES make sense to ask an output range, "hey, can I write to you multiple times?" So if you're expecting to be able to say void foo(R)(ref R range) if (isInputRange!R) { static if (hasLValueElements!R) // or whatever it was range.front = (ElementType!R).init; } then you're mixing up two completely unrelated/orthogonal concepts, which is silly. What you SHOULD be saying instead is: void foo(R)(ref R range) if (isInputRange!R) { static if (isOutputRange!R) // or whatever it was range.put((ElementType!R).init); }
Jul 09 2012
On 07/09/2012 06:05 PM, Mehrdad wrote:Consider the possibility that this electrical engineer might be a plumber as well.With your design you need to add something extra to allow writing to elements of a single-pass range.If that's the case, I'd hate to tell you this, but _unless_ you're planning on removing the notion of input/output ranges (and perhaps adding single-pass/multi-pass), you're doing it wrong. :-) That capability is simply /not needed/ when your /only/ contract is that something is an input range. Asking an input range, "hey, can I write to you?" is just... insulting! That's like asking an electrical engineer if he can plumb.
Jul 09 2012
On Monday, 9 July 2012 at 16:06:57 UTC, Timon Gehr wrote:Consider the possibility that this electrical engineer might be a plumber as well.Yes, so if you expect him to be, then you should ask him "are you a plumber?" (i.e. output range?) But what you're asking him right now is, "hey, can you do clear this pipe for me with copper wires??" which is downright silly. Being able to write to an output range does NOT require you to have 'ref' members. The only thing 'ref' ever buys you is performance, not any new capabilities. Depending on it doesn't make sense.
Jul 09 2012
On 07/09/2012 06:17 PM, Mehrdad wrote:On Monday, 9 July 2012 at 16:06:57 UTC, Timon Gehr wrote:The range is an input range of references. It could just as well be an input range of setters. I do not see why the notion of 'input range' should restrict the kinds of elements that are accessed by iterating the range. It would be downright silly.Consider the possibility that this electrical engineer might be a plumber as well.Yes, so if you expect him to be, then you should ask him "are you a plumber?" (i.e. output range?) But what you're asking him right now is, "hey, can you do clear this pipe for me with copper wires??" which is downright silly.Being able to write to an output range does NOT require you to have 'ref' members. The only thing 'ref' ever buys you is performance, not any new capabilities. Depending on it doesn't make sense.sort!"a[0]<b[0]"(zip(ra1,ra2));
Jul 09 2012
On Monday, 9 July 2012 at 16:25:06 UTC, Timon Gehr wrote:I do not see why the notion of 'input range' should restrict the kinds of elements that are accessed by iterating the range. It would be downright silly.Yes... but when did I ever impose anything on the /elements/? I never even used that word. I imposed constraints on the range, and the range only...sort!"a[0]<b[0]"(zip(ra1,ra2));sort(): "Sorts a random-access range according to predicate less." What does that have to do with input ranges?
Jul 09 2012
On 07/09/2012 06:27 PM, Mehrdad wrote:On Monday, 9 July 2012 at 16:25:06 UTC, Timon Gehr wrote:On 07/09/2012 06:04 PM, Mehrdad wrote:I do not see why the notion of 'input range' should restrict the kinds of elements that are accessed by iterating the range. It would be downright silly.Yes... but when did I ever impose anything on the /elements/?With your design you need to add something extra to allow writing to elements of a single-pass range.... That capability is simply /not needed/ when your /only/ contract is that something is an input range.I never even used that word.Unlikely.
Jul 09 2012
On Monday, 9 July 2012 at 16:42:52 UTC, Timon Gehr wrote:On 07/09/2012 06:04 PM, Mehrdad wrote:Huh? Isn't that Andrei's post?With your design you need to add something extra to allow writing to elements of a single-pass range.... That capability is simply /not needed/ when your /only/ contract is that something is an input range.I never even used that word.Unlikely.
Jul 09 2012
On 07/09/2012 06:47 PM, Mehrdad wrote:On Monday, 9 July 2012 at 16:42:52 UTC, Timon Gehr wrote:On 07/09/2012 11:14 AM, Mehrdad wrote:On 07/09/2012 06:04 PM, Mehrdad wrote:Huh? Isn't that Andrei's post?With your design you need to add something extra to allow writing to elements of a single-pass range.... That capability is simply /not needed/ when your /only/ contract is that something is an input range.I never even used that word.Unlikely.element
Jul 09 2012
On Monday, 9 July 2012 at 16:50:44 UTC, Timon Gehr wrote:On 07/09/2012 11:14 AM, Mehrdad wrote:Link to exact post please?element
Jul 09 2012
On Mon, 09 Jul 2012 12:17:49 -0400, Mehrdad <wfunction hotmail.com> wrote:On Monday, 9 July 2012 at 16:06:57 UTC, Timon Gehr wrote:import std.range; struct inputrange { int x; property ref int front() { return x;} void popFront() {} enum empty = false; } static assert(isOutputRange!(inputrange, int)); -SteveConsider the possibility that this electrical engineer might be a plumber as well.Yes, so if you expect him to be, then you should ask him "are you a plumber?" (i.e. output range?) But what you're asking him right now is, "hey, can you do clear this pipe for me with copper wires??" which is downright silly. Being able to write to an output range does NOT require you to have 'ref' members. The only thing 'ref' ever buys you is performance, not any new capabilities. Depending on it doesn't make sense.
Jul 16 2012
Perhaps if you could show an actual example of what you expect to be able to do, that would make things clearer?
Jul 09 2012
On Monday, 9 July 2012 at 16:22:05 UTC, Mehrdad wrote:Perhaps if you could show an actual example of what you expect to be able to do, that would make things clearer?It is useful to be able to write an algorithm that both reads and writes range elements. There are plenty of use cases for that, but if you really need an example, here's a simple one: void transform(alias f, R)(R r) if(isInputRange!R && hasAssignableElements!R) { for(; !r.empty; r.popFront()) r.front = unaryFun!f(r.front); }
Jul 09 2012
On Monday, 9 July 2012 at 18:33:26 UTC, jerro wrote:On Monday, 9 July 2012 at 16:22:05 UTC, Mehrdad wrote:Wow, thanks a bunch. That makes it a LOT more clearer than explanations. :) The trouble here is that the use case is valid, but the design of ranges doesn't really match it. Why? Because the way it currently stands, isInputRange && hasAssignableElements is basically isOutputRange (with also input capability), which doesn't make any sense. What we really need is to define a new kind of range -- perhaps an "input/output range", if I had to name it: it's forward-only, but it can be mutated and replaced. It'd be a superset of both input and output ranges, and also a subset of bidirectional ranges. But without formally defining a new kind of range, we're just saying "I want an input range that's also kinda-sorta an output range. Ooh, I also want to be able to mutate it! But I can't seek backwards, so it's not bidi. Hmm, not sure what to call it, so I'll just put constraints for everything I need and hope it makes sense." It (obviously) /works/, but it doesn't make sense semantically, the way it's defined right now.Perhaps if you could show an actual example of what you expect to be able to do, that would make things clearer?It is useful to be able to write an algorithm that both reads and writes range elements. There are plenty of use cases for that, but if you really need an example, here's a simple one: void transform(alias f, R)(R r) if(isInputRange!R && hasAssignableElements!R) { for(; !r.empty; r.popFront()) r.front = unaryFun!f(r.front); }
Jul 09 2012
On Monday, July 09, 2012 21:19:46 Mehrdad wrote:On Monday, 9 July 2012 at 18:33:26 UTC, jerro wrote:Except that how output ranges are written to and an input range with assignable elements are fundamentally different. Output ranges use put, which _might_ write to each individual element, or it may just append to the output range (it all depends on the implementation of put). You're essentially generating a range when you use an output range. But with an input range with assignable elements, you're specifically setting each element in an existing range rather than generating a new one. You can think of an output range like an output stream, whereas an input range with assignable elements is like an array where you're assigning values to each of its elements. - Jonathan M DavisOn Monday, 9 July 2012 at 16:22:05 UTC, Mehrdad wrote:Wow, thanks a bunch. That makes it a LOT more clearer than explanations. :) The trouble here is that the use case is valid, but the design of ranges doesn't really match it. Why? Because the way it currently stands, isInputRange && hasAssignableElements is basically isOutputRange (with also input capability), which doesn't make any sense.Perhaps if you could show an actual example of what you expect to be able to do, that would make things clearer?It is useful to be able to write an algorithm that both reads and writes range elements. There are plenty of use cases for that, but if you really need an example, here's a simple one: void transform(alias f, R)(R r) if(isInputRange!R && hasAssignableElements!R) { for(; !r.empty; r.popFront()) r.front = unaryFun!f(r.front); }
Jul 09 2012
On Monday, 9 July 2012 at 19:30:25 UTC, Jonathan M Davis wrote:Except that how output ranges are written to and an input range with assignable elements are fundamentally different. Output ranges use put, which _might_ write to each individual element, or it may just append to the output range (it all depends on the implementation of put). You're essentially generating a range when you use an output range. But with an input range with assignable elements, you're specifically setting each element in an existing range rather than generating a new one. You can think of an output range like an output stream, whereas an input range with assignable elements is like an array where you're assigning values to each of its elements.Oh!! So they're _exactly_ emulating C++ here, with insert_iterators trying to mimic regular iterators and all. I _never_ got that impression from the docs. The impression the docs give is that the only time when you "add" instead of "overwrite" is when it either doesn't make sense to overwrite (e.g. into hash set, at the end of an array, etc.). They never implied that you you might also be inserting into somewhere where overwriting is possible, so clearly I misunderstood what was intended. Btw, I just realized: With that explanation, hasAssignableElements doesn't make sense for some things that it should make sense for. How do you "assign" to e.g. a range of a BST? It _certainly_ makes sense to do a transform() on it, but assigning to any particular element doesn't make sense. So I think that confirms that what we really want IS an I/O range -- a range that supports delete-modify-add, but not (necessarily) read-modify-write (which is what hasAssignableElements implies). Makes sense?
Jul 09 2012
On 07/09/2012 09:46 PM, Mehrdad wrote:On Monday, 9 July 2012 at 19:30:25 UTC, Jonathan M Davis wrote:struct BST(K){ struct Node{ ... } Node root; auto opSlice(){ struct Range{ ... property front(){ return ...; } property front(K k){ deleteFront(); root.add(k); } } } }Except that how output ranges are written to and an input range with assignable elements are fundamentally different. Output ranges use put, which _might_ write to each individual element, or it may just append to the output range (it all depends on the implementation of put). You're essentially generating a range when you use an output range. But with an input range with assignable elements, you're specifically setting each element in an existing range rather than generating a new one. You can think of an output range like an output stream, whereas an input range with assignable elements is like an array where you're assigning values to each of its elements.Oh!! So they're _exactly_ emulating C++ here, with insert_iterators trying to mimic regular iterators and all. I _never_ got that impression from the docs. The impression the docs give is that the only time when you "add" instead of "overwrite" is when it either doesn't make sense to overwrite (e.g. into hash set, at the end of an array, etc.). They never implied that you you might also be inserting into somewhere where overwriting is possible, so clearly I misunderstood what was intended. Btw, I just realized: With that explanation, hasAssignableElements doesn't make sense for some things that it should make sense for. How do you "assign" to e.g. a range of a BST? It _certainly_ makes sense to do a transform() on it, but assigning to any particular element doesn't make sense. So I think that confirms that what we really want IS an I/O range -- a range that supports delete-modify-add, but not (necessarily) read-modify-write (which is what hasAssignableElements implies). Makes sense?
Jul 09 2012
On 07/09/2012 09:52 PM, Timon Gehr wrote:On 07/09/2012 09:46 PM, Mehrdad wrote:(but BSTs do not actually support a range that implements transform. Some elements might get transformed twice.)On Monday, 9 July 2012 at 19:30:25 UTC, Jonathan M Davis wrote:struct BST(K){ struct Node{ ... } Node root; auto opSlice(){ struct Range{ ... property front(){ return ...; } property front(K k){ deleteFront(); root.add(k); } } } }Except that how output ranges are written to and an input range with assignable elements are fundamentally different. Output ranges use put, which _might_ write to each individual element, or it may just append to the output range (it all depends on the implementation of put). You're essentially generating a range when you use an output range. But with an input range with assignable elements, you're specifically setting each element in an existing range rather than generating a new one. You can think of an output range like an output stream, whereas an input range with assignable elements is like an array where you're assigning values to each of its elements.Oh!! So they're _exactly_ emulating C++ here, with insert_iterators trying to mimic regular iterators and all. I _never_ got that impression from the docs. The impression the docs give is that the only time when you "add" instead of "overwrite" is when it either doesn't make sense to overwrite (e.g. into hash set, at the end of an array, etc.). They never implied that you you might also be inserting into somewhere where overwriting is possible, so clearly I misunderstood what was intended. Btw, I just realized: With that explanation, hasAssignableElements doesn't make sense for some things that it should make sense for. How do you "assign" to e.g. a range of a BST? It _certainly_ makes sense to do a transform() on it, but assigning to any particular element doesn't make sense. So I think that confirms that what we really want IS an I/O range -- a range that supports delete-modify-add, but not (necessarily) read-modify-write (which is what hasAssignableElements implies). Makes sense?
Jul 09 2012
On Monday, 9 July 2012 at 15:34:35 UTC, Mehrdad wrote:Think: CD burning.A good comparison: Single-pass output ranges: an output range that writes to a CD, DVD, etc. Multi-pass output ranges: an output range that writes to a tape, hard disk, etc.
Jul 09 2012
On Monday, 9 July 2012 at 15:34:35 UTC, Mehrdad wrote:On Monday, 9 July 2012 at 15:16:52 UTC, Andrei Alexandrescu wrote:template isOutputRange(R,E) Returns true if R is an output range for elements of type E. An output range is defined functionally as a range that supports the operation void put(R, E)(ref R r, E e);the only primitive of output ranges is "put". AndreiSorry? I don't know what you mean
Jul 09 2012
On 2010-07-12 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> said:T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null.This can't be safe. getNext would need to take a pointer out of the item reference, which isn't allowed in SafeD. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 13 2010
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases. Consider this: T* getNext(R, T)(ref R range, ref T item); Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item. In all cases, when there are no more elements in the range, getNext returns null. getNext is easy to define for e.g. arrays and files. How does it sound? Does it bring significant simplification?I gave it a try, and it fairly simplified range implementation. http://gist.github.com/474562 I think it's good. (1) But how does it represent an infinite range? (2) Should user code use getNext for input ranges (like put for output ranges)? For example: void doSomething(R)(R input) { // read first character if any dchar* p = getNext!dchar(input); if (p == null) return; dchar a = *p; // read subsequent characters... dchar b = *enforce( getNext!dchar(input) ); ... } Shin
Jul 14 2010
On Tuesday, 13 July 2010 at 03:48:08 UTC, Andrei Alexandrescu wrote:I think I figured out a comfortable and all-encompassing means to define a simplified interface for an input range. Currently input ranges need to define empty, front, and popFront. That works but it's a bit heavy for simple input ranges. We've been discussing simplified interfaces in this group but couldn't find one that satisfied all use cases.This feels quite similar to "consumeFront"? As a matter of fact, isn't it just exchanging: "Check Not Empty" then "consumeFront" for "getNext" then "checkNotNull"Semantics: if the range wants to expose addresses of its elements, it returns a pointer to the current element and also advances to the next element. Otherwise (i.e. the range does not have or does not want to expose addresses of its elements), the range fills "item" with the current value, again moves on to the next value, and returns &item.My big worry here is that when the range does _not_ want to provide a pointer to its internals, the caller has no way of knowing it. Modifying the pointed object may or may not modify the range. For example, "std.algorithm.map" on an Array!bool simply can't work with getNext. Since the implementer has no idea what he is operating on, the conclusion is that he simply can't use getNext when mutation is possible.
Jul 09 2012
On Monday, 9 July 2012 at 08:55:21 UTC, monarch_dodra wrote:For example, "std.algorithm.map" on an Array!bool simply can't work with getNext. Since the implementer has no idea what he is operating on, the conclusion is that he simply can't use getNext when mutation is possible.Re-reading the documentation of "map", I realize this is a bad example, but you get the point. "initializeAll" would be a better example (I think).
Jul 09 2012
On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Consider this: T* getNext(R, T)(ref R range, ref T item);I see you still defending this concept from 2010, does it mean we may still get it? (it would make things easier for std.io for sure) If so, can we ditch the notion of forward+ ranges requiring save? -Steve
Jul 16 2012
On 7/16/12 5:55 PM, Steven Schveighoffer wrote:On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I'm not defending it, I recall last time I discussed it I mentioned its issues. The main one is it makes it awkward to iterate ranges that don't actually have elements in memory. The approach would be forced to return the same pointer to a buffer, until the end when it returns null. User code would have no ability to make the distinction. On the other hand this is what e.g. byLine does, but with an array, not a pointer. If you feel strongly there are merits in the approach that overcome its issues, why not design around it? Anyhow, it looks like this would be too big a redesign of input ranges. Like it or not, .save is here to stay. AndreiConsider this: T* getNext(R, T)(ref R range, ref T item);I see you still defending this concept from 2010, does it mean we may still get it? (it would make things easier for std.io for sure) If so, can we ditch the notion of forward+ ranges requiring save? -Steve
Jul 16 2012
On Mon, 16 Jul 2012 18:37:34 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 7/16/12 5:55 PM, Steven Schveighoffer wrote:Yes, that is a true drawback. I suppose a nullable type would help here.On Mon, 12 Jul 2010 23:48:05 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I'm not defending it, I recall last time I discussed it I mentioned its issues. The main one is it makes it awkward to iterate ranges that don't actually have elements in memory. The approach would be forced to return the same pointer to a buffer, until the end when it returns null. User code would have no ability to make the distinction.Consider this: T* getNext(R, T)(ref R range, ref T item);I see you still defending this concept from 2010, does it mean we may still get it? (it would make things easier for std.io for sure) If so, can we ditch the notion of forward+ ranges requiring save? -SteveOn the other hand this is what e.g. byLine does, but with an array, not a pointer.True, but I was thinking in terms of unbuffered i/o. Buffered i/o fits in ok with the current range scheme, since, as you point out, a slice can be a valid range element. Come to think of it, you still need a place to put the data, so it doesn't really matter. You always have to have a buffer to point to. So maybe this isn't such a good idea...Like it or not, .save is here to stay.not. ;) -Steve
Jul 16 2012