Problem with C++ ranges also exhibited by D

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Apr 16 16:29:44 UTC 2019


On Tue, Apr 16, 2019 at 12:07:03PM +0000, Atila Neves via Digitalmars-d wrote:
> This blog post shows off some problems with the ranges v3 library that
> is going to be included in the C++20 standard library:
> 
> https://www.fluentcpp.com/2019/04/16/an-alternative-design-to-iterators-and-ranges-using-stdoptional/
> 
> Because the problem is basically (from the blog) "Is it really
> necessary to have separate operations for advancing the iterator and
> evaluating its element?". The answer is of course no, and this has
> been brought up in the forums before.

Actually, I've used libraries in the past that combine advancing the
range with reading the next element.  There's certainly a benefit when
you're doing simple iteration and don't want to bother to have to
manually bump the range everywhere.  However, I found that for
non-trivial tasks I end up sprinkling ad hoc caching code everywhere,
just because reading the front of a range is conflated with advancing
it.  One of the most annoying examples of this sort is the Posix file
API, where .eof is uncheckable until you perform a read operation, and
read may arbitrarily block, resulting in a percolation of corner cases,
workarounds, and other band-aid just to make it work nicely with code
that needs to check EOF without blocking / consuming the front of the
data.

For this reason, I found D's range API much cleaner to use, despite
being more verbose.  D's range API puts the onus on the range author to
write correct code to retain the value of .front; the other API forces
the client code to cache values when needed, violating DRY, and client
code doesn't always do it right.


> Like it or not, D has the same issue:
> 
> -----------------------------
> import std.algorithm;
> import std.range;
> import std.stdio;
> 
> void main() {
>     iota(1, 6)
>         .map!((n) { writeln("transform ", n); return n * 2; })
>         .filter!(n => n % 4 == 0)
>         .writeln;
> }
> -----------------------------
> 
> Produces the output:
> 
> [transform 1
> transform 2
> transform 2
> 4transform 3
> transform 4
> , transform 4
> 8transform 5
> ]
> 
> Showing that the mapped function is called more times than strictly
> necessary, just as in C++.
[...]

Just use .cache and move on already! ;-)


T

-- 
In theory, software is implemented according to the design that has been carefully worked out beforehand. In practice, design documents are written after the fact to describe the sorry mess that has gone on before.


More information about the Digitalmars-d mailing list