www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - range practicle use

reply spir <denis.spir gmail.com> writes:
Hello,


In the course of a project (1) 2 partner D programmers and myself are curre=
ntly implementing, we faced 2 issues which prevented us using a range inter=
face as planned. We initially intended to do it for better compliance with =
D's coming new style, and nice inter-relation with Phobos modules like std.=
algorithm. Instead, we use opApply to implement traversal; which does the j=
ob.
This post's purpose is to expose these problems to help and making range in=
terfaces better usable, or simply usable, in various practical cases. (Opin=
ions are mine --I don't known my partners' exact position, except that they=
 indeed agree these topics plainly prevent us using ranges.)
To illustrate the situation, please see an artificial example coded below (=
the real case would require explaining several irrelevant points).


-1- textual output
Functions like writeln silently use the formatValue set of templates in std=
.format, to get a textual form for the element to be output. Template const=
raints determine which version is used for a given kind of element. As impl=
emented now:
* There are formatValue variants for elements that implement a range interf=
ace (which produce an array-like form).
* Constraint selection for a range interface has high precedence; especiall=
y it shortcuts any toString function explicitely implemented by the program=
mer.=20

This prevents a user to define any adequate output format for the elements =
of a custom type, if the type also happens to implement a range interface. =
A critical bug, and an absolute blocker for us.

Actually, this cannot even work in numerous cases. In our project, the rang=
e's ElementType (as returned by opindex & front) would happen to be identic=
al to the range type itself. In this case, the formatting algorithm via a r=
ange runs into an infinite loop. It is instead necessary to use the program=
mer-defined format in toString (or later writeTo).
In the example below, trying to write() an element yields an infinite serie=
s of "[[[...", in trying to print it like an array.

Note that it is a common situation: for instance, (textual) string types li=
ke ones in high-level languages do not expose a separate element type (char=
acter); instead, a character is just a singleton of the type. Even more com=
monly, list, tree, graph nodes usually are of the same type as the containe=
r (or the root/container is of a sub-type implementing global methods).

Another problem is that if the type is a class (as opposed to a struct), th=
en defining a range interface introduces a template-selection conflict with=
 the standard class case: as of now, they are not mutually exclusive, leadi=
ng to compile-time error.

Anyway, what is the need for a range-specific output format? And why should=
 it exactly look like an array (misleading)? Structs and classes also imple=
menting ranges can define toString (later writeTo) for an accurate format; =
and in case this would be the right choice, reproducing an array-like forma=
t is a few lines of code. The case of ranges cannot compare to the one of a=
rrays (which are _only_ arrays, which form a much more narrow kind of objec=
ts, and for which one cannot define any format). For all these reasons, I w=
ould happily get rid of these range-specific formats, that introduce compli=
cation, issues and bugs.
Or: these issues must be solved by complicating constraints (in particular,=
 the range case must check no toString is defined).

[you can comment on issue http://d.puremagic.com/issues/show_bug.cgi?id=3D5=
354]


-2- indexed iteration

It seems there is no way to directly define indexed iteration using ranges,=
 like commonly needed by:
    foreach(index,element ; collection) {...}

A possible but inadequate workaround would be to define a tool type for thi=
s:
    struct TraversalPair (Element) {uint index ; Element element;}
Then define the range's element output routines (opIndex, front, back) to r=
eturn pairs instead of elements; and use this like:
    foreach (pair ; collection) {actWith(pair.element, pair.index);}
But this requires a client programmer to know this particularity; and anywa=
y does not fit D common style and practice, I guess.

How to solve this practically? I would be happy with the above workaround i=
f it became a commonly used solution, supported by the stdlib and if necess=
ary by the core language. This may scale by defining the tool type as a sup=
erclass instead, allowing various variants, possibly with more elements.
Maybe an alternative is to use tuples: allow variants of opIndex, front, an=
d back, to return (index,element) tuples and let the compiler use these ove=
rloads when the client code requests 2 (or more) returned values.
The first solution is more explicite, and possibly general; the second may =
fit common practice better if/when tuples become widely used (a rather far =
& hypothetical future ;-) ?).


Thank you,
Denis


=3D=3D=3D example using range =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
struct Text {
    uint length;
    private char[] chars;
    private uint index;     // for input range
    // construction
    this (string s=3Dnull) {
        this.length =3D s.length;
        this.chars =3D s.dup;
    }
    this (char ch) {
        this.length =3D 1;
        this.chars =3D [ch];
    }
    // output
    string toString () {
        return format(`"%s"`, this.chars);
    }
    // range
    string opIndex (uint index) {
        return String(this.chars[index]);
    }
     property void popFront () {
        ++ this.index;
    }
     property bool empty () {
        return (this.index >=3D this.length);
    }
     property String front () {
        return String(this.chars[this.index]);
    }
    // ...operational methods...
}

=3D=3D=3D solution using opApply =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
struct Text {
    uint length;
    private char[] chars;
    private uint index;     // for input range
    // construction
    this (string s=3Dnull) {
        this.length =3D s.length;
        this.chars =3D s.dup;
    }
    this (char ch) {
        this.length =3D 1;
        this.chars =3D [ch];
    }
    // output
    string toString () {
        return format(`"%s"`, this.chars);
    }
    // indexing
    string opIndex (uint index) {
        return String(this.chars[index]);
    }
    // traversal (both plain & indexed uses)
    int opApply (int delegate (ref Text) block) {
        // not considering result
        foreach (_,ch ; this.chars)
            block(Text(ch));
        return 0;
    }
    int opApply (int delegate (ref uint, ref Text) block) {
        // not considering result
        foreach (i,ch ; this.chars)
            block(i, Text(ch));
        return 0;
    }
    // ...operational methods...
}
-- -- -- -- -- -- --
vit esse estrany =E2=98=A3

spir.wikidot.com
Dec 30 2010
next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Thu, 30 Dec 2010 05:41:18 -0700, spir <denis.spir gmail.com> wrote:
 Hello,


 In the course of a project (1) 2 partner D programmers and myself are  
 currently implementing, we faced 2 issues which prevented us using a  
 range interface as planned. We initially intended to do it for better  
 compliance with D's coming new style, and nice inter-relation with  
 Phobos modules like std.algorithm. Instead, we use opApply to implement  
 traversal; which does the job.
 This post's purpose is to expose these problems to help and making range  
 interfaces better usable, or simply usable, in various practical cases.  
 (Opinions are mine --I don't known my partners' exact position, except  
 that they indeed agree these topics plainly prevent us using ranges.)
 To illustrate the situation, please see an artificial example coded  
 below (the real case would require explaining several irrelevant points).


 -1- textual output
[snip] I too have been bitten by this bug. Specifically, when using opDispatch you have to remember to disable all of the range interfaces.
 -2- indexed iteration

 It seems there is no way to directly define indexed iteration using  
 ranges, like commonly needed by:
     foreach(index,element ; collection) {...}

 A possible but inadequate workaround would be to define a tool type for  
 this:
     struct TraversalPair (Element) {uint index ; Element element;}
 Then define the range's element output routines (opIndex, front, back)  
 to return pairs instead of elements; and use this like:
     foreach (pair ; collection) {actWith(pair.element, pair.index);}
 But this requires a client programmer to know this particularity; and  
 anyway does not fit D common style and practice, I guess.

 How to solve this practically? I would be happy with the above  
 workaround if it became a commonly used solution, supported by the  
 stdlib and if necessary by the core language. This may scale by defining  
 the tool type as a superclass instead, allowing various variants,  
 possibly with more elements.
 Maybe an alternative is to use tuples: allow variants of opIndex, front,  
 and back, to return (index,element) tuples and let the compiler use  
 these overloads when the client code requests 2 (or more) returned  
 values.
 The first solution is more explicite, and possibly general; the second  
 may fit common practice better if/when tuples become widely used (a  
 rather far & hypothetical future ;-) ?).


 Thank you,
 Denis
I'd prefer the tuple solution, particularly as tuples + syntactic sugar have been discussed before regarding multiple value return.
Dec 30 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/30/10 6:41 AM, spir wrote:
 Hello,


 In the course of a project (1) 2 partner D programmers and myself are
 currently implementing, we faced 2 issues which prevented us using a
 range interface as planned. We initially intended to do it for better
 compliance with D's coming new style, and nice inter-relation with
 Phobos modules like std.algorithm. Instead, we use opApply to
 implement traversal; which does the job. This post's purpose is to
 expose these problems to help and making range interfaces better
 usable, or simply usable, in various practical cases. (Opinions are
 mine --I don't known my partners' exact position, except that they
 indeed agree these topics plainly prevent us using ranges.)
[snip] Thanks very much for taking the time to document your experience. This is very useful.
 -1- textual output
 Functions like writeln silently use the formatValue set of templates
in std.format, to get a textual form for the element to be output. Template constraints determine which version is used for a given kind of element. As implemented now:
 * There are formatValue variants for elements that implement a range
interface (which produce an array-like form).
 * Constraint selection for a range interface has high precedence;
especially it shortcuts any toString function explicitely implemented by the programmer.
 This prevents a user to define any adequate output format for the
elements of a custom type, if the type also happens to implement a range interface. A critical bug, and an absolute blocker for us. Seeing "absolute blocker" here makes me think: is there /absolutely no way/ to get that particular task done in the D programming language? I might be off here because I don't have details, but my knee-jerk reaction is: struct MyRange { ... } void print(MyRange r) { ... } Of course it would be great if you could use the predefined write/writeln functions, and we should improve them, but I have a hard time considering this an absolute blocker. You write a function once and then you call it. I do that in my C++ code all the time. Iostreams are terrible (to be euphemistic), but they don't prevent me and others from using C++.
 Actually, this cannot even work in numerous cases. In our project, the range's
ElementType (as returned by opindex&  front) would happen to be identical to
the range type itself. In this case, the formatting algorithm via a range runs
into an infinite loop.
Ha, glad you mention that. When I first designed ranges I had this fear in a couple of places: if a range is its own element type, then there's a problem. I deferred my solution about that to later. Apparently that later has come now.
 It is instead necessary to use the programmer-defined format in toString (or
later writeTo).
 In the example below, trying to write() an element yields an infinite series
of "[[[...", in trying to print it like an array.
Yep, yep, great feedback. This is a fixed point in the iteration typeof(r) -> typeof(r.front), which is not difficult to detect. One question would be - what to do when we _do_ detect that? (One thing that should be kept in mind is that ranges are printed the way they are simply because I had to choose _something_. There is definitely a lot of room for improvement there in many aspects.)
 Note that it is a common situation: for instance, (textual) string types like
ones in high-level languages do not expose a separate element type (character);
instead, a character is just a singleton of the type. Even more commonly, list,
tree, graph nodes usually are of the same type as the container (or the
root/container is of a sub-type implementing global methods).
This is interesting, but I find it difficult to imagine concrete cases because in all my code the iteration strategy is encoded in the range's type. For example, splitter() returns a range that iterates a string by words. Arguably the input and output have the same type, but the type of the range is _not_ string, it's Splitter!string or whatever. Similarly, for a self-referential data structure, the range type would be e.g. InOrder!TreeNode, PostOrder!TreeNode etc. I guess there are legitimate cases in which the TreeNode has front() that returns itself and so on, so we need to address this.
 Another problem is that if the type is a class (as opposed to a struct), then
defining a range interface introduces a template-selection conflict with the
standard class case: as of now, they are not mutually exclusive, leading to
compile-time error.
I don't understand this, could you please give a bit of detail?
 Anyway, what is the need for a range-specific output format? And why should it
exactly look like an array (misleading)?
Because I needed to put something there and couldn't think of something better...
 Structs and classes also implementing ranges can define toString (later
writeTo) for an accurate format; and in case this would be the right choice,
reproducing an array-like format is a few lines of code.
Agreed. writeTo() with a delegate taking const(char)[] has been discussed and I think is a good solution.
 -2- indexed iteration

 It seems there is no way to directly define indexed iteration using ranges,
like commonly needed by:
      foreach(index,element ; collection) {...}
Aha! The time of reckoning has come :o).
 A possible but inadequate workaround would be to define a tool type for this:
      struct TraversalPair (Element) {uint index ; Element element;}
 Then define the range's element output routines (opIndex, front, back) to
return pairs instead of elements; and use this like:
      foreach (pair ; collection) {actWith(pair.element, pair.index);}
 But this requires a client programmer to know this particularity; and anyway
does not fit D common style and practice, I guess.
Clearly that's an annoying limitation of ranges when compared against opApply, and we need to fix that. That being said, I see your example uses the index as a simple counter 0, 1, 2, ... so again putting my "could this or could this not be done in the D programming language?" I can't stop thinking about this: size_t index = 0; foreach (e; collection) { ... ++index; } You need to mind the presence of "continue" and the larger scope of "index", but these are simple matters. Annoying? Definitely. Makes ranges unusable? Perhaps not as much.
 How to solve this practically? I would be happy with the above workaround if
it became a commonly used solution, supported by the stdlib and if necessary by
the core language. This may scale by defining the tool type as a superclass
instead, allowing various variants, possibly with more elements.
 Maybe an alternative is to use tuples: allow variants of opIndex, front, and
back, to return (index,element) tuples and let the compiler use these overloads
when the client code requests 2 (or more) returned values.
 The first solution is more explicite, and possibly general; the second may fit
common practice better if/when tuples become widely used (a rather far& 
hypothetical future ;-) ?).
I'd think tuples are the simplest solution and the basis of the language change that fixes this insufficiency. First, if all you want is an index you can write this: foreach (e; zip(iota(r.length), r)) { // e[0] is 0, 1, 2, ... // e[1] is the current range element } If the range doesn't have a length you can use iota with the largest number - iteration will stop at the shortest: foreach (e; zip(iota(size_t.max), r)) { // e[0] is 0, 1, 2, ... // e[1] is the current range element } If such uses of iota become common we could make iota() with no arguments just go 0, 1, 2,..., size_t.max, 0, 1, 2,... Second, if you want to do some cleverer stuff than just 0, 1, 2,... you could always have r.front yield a tuple: struct MyRange { property Tuple!(string, double) front() { ... } ... } foreach (e; r) { // e[0] is the current string // e[1] is the current double } This is also the basis of a language solution. Currently the language lowers foreach for ranges down to a loop that uses front(), popFront(), and empty(). We discussed in this group a while ago that foreach with multiple iteration elements (e.g. a, b, c) could be lowered by binding a to r.front[0], b to r.front[1], and c to r.front[2 .. $]. This is compatible with the current lowering and allows convenient usage with ranges that have either tuples or arrays as their front. Thanks again for this report. We definitely need to work on all of the above. Let me ask you this - did the project actually need to use algorithms in std.algorithm? Based on my experience, my assumption is that you didn't need the more complex algorithms (e.g. sort or bringToFront) at all, and that you didn't have intensive use for the simpler algorithms (map, reduce) either. This is because if you did, ranges would have had enough strategic advantage for you, and consequently you would have been much more inclined to work around the issues mentioned above. As such, it's possible that you started with ranges because they sounded nice to have but didn't quite find a solid need for their advantages over e.g. opApply. Please let me know - thanks. Andrei
Dec 30 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Andrei:

 Anyway, what is the need for a range-specific output format? And why should it
exactly look like an array (misleading)?
Because I needed to put something there and couldn't think of something better...
In a bug report I have suggested to print: [1; 2; 3] Instead of: [1, 2, 3] To tell apart the two different things. Bye, bearophile
Dec 30 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Thu, 30 Dec 2010 11:19:33 -0600
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:

 On 12/30/10 6:41 AM, spir wrote:
 Hello,


 In the course of a project (1) 2 partner D programmers and myself are
 currently implementing, we faced 2 issues which prevented us using a
 range interface as planned. We initially intended to do it for better
 compliance with D's coming new style, and nice inter-relation with
 Phobos modules like std.algorithm. Instead, we use opApply to
 implement traversal; which does the job. This post's purpose is to
 expose these problems to help and making range interfaces better
 usable, or simply usable, in various practical cases. (Opinions are
 mine --I don't known my partners' exact position, except that they
 indeed agree these topics plainly prevent us using ranges.)
[snip] =20 Thanks very much for taking the time to document your experience. This=20 is very useful. =20
 -1- textual output
 Functions like writeln silently use the formatValue set of templates
in std.format, to get a textual form for the element to be output. Template constraints determine which version is used for a given kind of element. As implemented now:
 * There are formatValue variants for elements that implement a range
interface (which produce an array-like form).
 * Constraint selection for a range interface has high precedence;
especially it shortcuts any toString function explicitely implemented by the programmer.
 This prevents a user to define any adequate output format for the
elements of a custom type, if the type also happens to implement a range interface. A critical bug, and an absolute blocker for us. =20 Seeing "absolute blocker" here makes me think: is there /absolutely no=20 way/ to get that particular task done in the D programming language? I=20 might be off here because I don't have details, but my knee-jerk=20 reaction is: =20 struct MyRange { ... } void print(MyRange r) { ... }
Absolute blocker is too strong ;-) But it is still, say, a _real_ blocker; = because the project in question is a library (see below). Thus, we consider= not only our needs (for which a custom print indeed does the job), but als= o client programmers' needs, practices, expectations. =46rom this point of view, we cannot (or rather I do not want to) force users= of the lib writing such workarounds, but instead provide them with typical= D facilities. For the same reason we initially planned to use ranges ;-)
 [...]=20
 Actually, this cannot even work in numerous cases. In our project, the =
range's ElementType (as returned by opindex& front) would happen to be ide= ntical to the range type itself. In this case, the formatting algorithm via= a range runs into an infinite loop.
=20
 Ha, glad you mention that. When I first designed ranges I had this fear=20
 in a couple of places: if a range is its own element type, then there's=20
 a problem. I deferred my solution about that to later. Apparently that=20
 later has come now.
=20
 It is instead necessary to use the programmer-defined format in toStrin=
g (or later writeTo).
 In the example below, trying to write() an element yields an infinite s=
eries of "[[[...", in trying to print it like an array.
=20
 Yep, yep, great feedback. This is a fixed point in the iteration=20
 typeof(r) -> typeof(r.front), which is not difficult to detect.
Exactly.
 One=20
 question would be - what to do when we _do_ detect that?
=20
 (One thing that should be kept in mind is that ranges are printed the=20
 way they are simply because I had to choose _something_. There is=20
 definitely a lot of room for improvement there in many aspects.)
1. Why do you think we need a _default_ output format for ranges? (We can a= lways use toString or writeTo) Again, the case of ranges is completely diff= erent from the one of arrays. Ranges are (1) one aspect of given types (2) = very diverse, even structurally (3) able to define toString. I personly wou= ld never need such a default format. 2. If ever such a default format for ranges is still considered a good thin= g, the template selection constraints must exclude cases where (1) toString= is defined (2) the range's ElementType is identical to the type defining t= he range. this gets a bit more complicated with structs' .stringof. And there's also = a distinct issue with classes -- see below. (I implemented the change when I discovered the issue, but could not succee= d in linking for seemingly unrelated reasons.)
 Note that it is a common situation: for instance, (textual) string type=
s like ones in high-level languages do not expose a separate element type (= character); instead, a character is just a singleton of the type. Even more= commonly, list, tree, graph nodes usually are of the same type as the cont= ainer (or the root/container is of a sub-type implementing global methods).
=20
 This is interesting, but I find it difficult to imagine concrete cases=20
 because in all my code the iteration strategy is encoded in the range's=20
 type. For example, splitter() returns a range that iterates a string by=20
 words. Arguably the input and output have the same type, but the type of=
=20
 the range is _not_ string, it's Splitter!string or whatever. Similarly,=20
 for a self-referential data structure, the range type would be e.g.=20
 InOrder!TreeNode, PostOrder!TreeNode etc. I guess there are legitimate=20
 cases in which the TreeNode has front() that returns itself and so on,=20
 so we need to address this.
Hum, maybe I have a somewhat different vision of ranges as the one you have= in mind as typical case. For me, a range interface is mainly a utility pro= viding iteration for a type -- whatever the type is and does. It is an _asp= ect_ of said type. just like opApply, simply more diverse, general, and use= ful (notably for algorithmic needs). You seem to consider ranges as types by themselves. Then, yes, it makes mor= e sense to have a default format, for instance. On a tree structure i would like to define a range interface so that people= could, for instance, print its nodes in sequence, filter them, or use a cu= stom sort.
 Another problem is that if the type is a class (as opposed to a struct)=
, then defining a range interface introduces a template-selection conflict = with the standard class case: as of now, they are not mutually exclusive, l= eading to compile-time error.
=20
 I don't understand this, could you please give a bit of detail?
I guess (not really tested) any class defining a range interface runs into = the compiler error. A nearly not artificial example: struct Pixel {int x,y;} class Image { Pixel[] pixels; private uint index =3D 0; this (Pixel[] pixels) {this.pixels =3D pixels;} // range interface property void popFront () {++ this.index;} property bool empty () {return (this.index >=3D this.pixels.length);} property Pixel front () {return this.pixels[this.index];} // ... operational methods ... } unittest { auto image =3D new Image([Pixel(1,2),Pixel(3,4),Pixel(5,6),]); writeln(image); } =3D=3D> rdmd -w -debug -unittest -L--export-dynamic --build-only -of"__trials__" "_= _trials__.d" /usr/include/d/dmd/phobos/std/format.d(1404): Error: template std.format.fo= rmatValue(Writer,T,Char) if (is(const(T) =3D=3D const(void[]))) formatValue= (Writer,T,Char) if (is(const(T) =3D=3D const(void[]))) matches more than on= e template declaration, /usr/include/d/dmd/phobos/std/format.d(1109):format= Value(Writer,T,Char) if (isInputRange!(T) && !isSomeChar!(ElementType!(T)))= and /usr/include/d/dmd/phobos/std/format.d(1260):formatValue(Writer,T,Char= ) if (is(T =3D=3D class)) Compilation failed. (I love this error message -- it's a single line) Note that I did not even define toString on the class.
 [...]
 -2- indexed iteration

 It seems there is no way to directly define indexed iteration using ran=
ges, like commonly needed by:
      foreach(index,element ; collection) {...}
=20 Aha! The time of reckoning has come :o). =20
 A possible but inadequate workaround would be to define a tool type for=
this:
      struct TraversalPair (Element) {uint index ; Element element;}
 Then define the range's element output routines (opIndex, front, back) =
to return pairs instead of elements; and use this like:
      foreach (pair ; collection) {actWith(pair.element, pair.index);}
 But this requires a client programmer to know this particularity; and a=
nyway does not fit D common style and practice, I guess.
=20
 Clearly that's an annoying limitation of ranges when compared against=20
 opApply, and we need to fix that. That being said, I see your example=20
 uses the index as a simple counter 0, 1, 2, ... so again putting my=20
 "could this or could this not be done in the D programming language?" I=20
 can't stop thinking about this:
=20
 size_t index =3D 0;
 foreach (e; collection) {
      ...
      ++index;
 }
=20
 You need to mind the presence of "continue" and the larger scope of=20
 "index", but these are simple matters.
=20
 Annoying? Definitely. Makes ranges unusable? Perhaps not as much.
You are completely, and that's what we are used to do. But again user expec= tations when using a D lib have some weight on one side of the balance. I think (maybe wrongly) this issue will have to be solved one day or the ot= her when the community grows, people regurarly use ranges and... voice thei= r demands. (Better think at it in advance to explore and _test_ various sol= utions in practice without pressure.)
 How to solve this practically? I would be happy with the above workarou=
nd if it became a commonly used solution, supported by the stdlib and if ne= cessary by the core language. This may scale by defining the tool type as a= superclass instead, allowing various variants, possibly with more elements.
 Maybe an alternative is to use tuples: allow variants of opIndex, front=
, and back, to return (index,element) tuples and let the compiler use these= overloads when the client code requests 2 (or more) returned values.
 The first solution is more explicite, and possibly general; the second =
may fit common practice better if/when tuples become widely used (a rather = far& hypothetical future ;-) ?).
=20
 I'd think tuples are the simplest solution and the basis of the language=
=20
 change that fixes this insufficiency.
Right. (I'm personly not fan of tuples because they don't say what they hol= d -- as opposed to struct-like named tuple: t[0] vs p.index--, but it's not= major issue: I won't discuss it ;-)
 First, if all you want is an index you can write this:
=20
 foreach (e; zip(iota(r.length), r))
 {
      // e[0] is 0, 1, 2, ...
      // e[1] is the current range element
 }
Yes, but if you write a lib and have an alternative solution providing inde= xed iteration to the lib's clients out of the bocx, what do you choose? Another point is "index" in the wider sense of symbol/ref/entry id or key. = just like for an associative array, it's not always an ordinal (and even no= t always sequential so that a widened notion of iota could not always do th= e job).
 [More brain production toward a solution.]
 Thanks again for this report. We definitely need to work on all of the=20
 above. Let me ask you this - did the project actually need to use=20
 algorithms in std.algorithm? Based on my experience, my assumption is=20
 that you didn't need the more complex algorithms (e.g. sort or=20
 bringToFront) at all, and that you didn't have intensive use for the=20
 simpler algorithms (map, reduce) either. This is because if you did,=20
 ranges would have had enough strategic advantage for you, and=20
 consequently you would have been much more inclined to work around the=20
 issues mentioned above. As such, it's possible that you started with=20
 ranges because they sounded nice to have but didn't quite find a solid=20
 need for their advantages over e.g. opApply. Please let me know - thanks.
Sorry, forgot to say 2 words about that in the OP. The lib in question is a UCS/Unicode toolkit evoked 2-3 on the list. With 2= major aspects: * A set of tools (normalisation, casing, sorting...) called "dunicode", iss= ued from a previous by Stephan Mueller & Ivan Melchunik. (Who let it aside = waiting for D2 stabilisation, but reactivated its unicode part after seeing= my posts and contacting me.) * A higher-level universal "Text" type, using some of those routines, and p= ossibly later used by some other ones. Both parts are in good advancement stage (actually usable), we were waiting= for merging code and structure before publishing information. (As of now, = Text is for instance stand-alone, which is stupid not only for repetition, = also because their algorithms and data structures are or will be better tha= n mine.) For curious people: dunicode: https://bitbucket.org/stephan/dunicode/src Text: https://bitbucket.org/denispir/denispir-d/src (A presentation for Text is at https://bitbucket.org/denispir/denispir-d/sr= c/b543fb352803/U%20missing%20level%20of%20abstraction. I'm not yet happy wi= th it -- feedback welcome.) Now, about your question: We 3 do not strictly need much; except if we want to provide default string= sorting, for instance. we can indeed work around issues for us, for our ow= n needs, not for the users' needs. But a range interface and its side effect on integration with the rest of t= he 'new' D practice and facilities (esp algorithmic) is, i guess, a great g= ain for clients of a lib. Hope I'm clear. I even think _this_ precisely is = the point of having generalisations like ranges, higher-order funcs, or gen= erics, built in the core of a language's toolkit. But that's only me. Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010