A Small Contribution to Phobos

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Jun 2 09:55:23 PDT 2013


On 6/2/13 11:41 AM, monarch_dodra wrote:
> On Sunday, 2 June 2013 at 13:07:18 UTC, Andrei Alexandrescu wrote:
>> [1, 2, 3, 4].map!(n => n.writeln).reduce;
>>
>>
>> Andrei
>
> One of the problems with using "map" for something such as this, is that
> the resulting object is not a range, since "front" now returns void, and
> a range *must* return a value. So that code will never compile (since
> reduce will ask for at least input range). Heck, I think we should make
> it so that map refuses to compile with an operator that returns void. It
> doesn't make much sense as-is.

Hm, interesting. I'm destroyed.

> Usage has to be something like:
>
> map!((n) {n.writeln; return n;})
>
> which is quite clunky. The idea of a "tee" range, that takes n, runs an
> operation on it, and then returns said n as is becomes really very
> useful (and more idiomatic). [1, 2, 3, 4].tee!(n => n.writeln). There!
> perfect :)
>
> I've dabbled in implementing such a function, but there are conceptual
> problems: If the user calls "front" twice in a row, then should "fun" be
> called twice? If user popsFront without calling front, should "fun" be
> called at all?
>
> Should it keep track of calls, to guarantee 1, and only 1, call on each
> element?
>
> I'm not sure there is a correct answer to that, which is one of the
> reasons I haven't actually submitted anything.

I think there is one answer that arguably narrows the design space 
appropriately: just like the Unix utility, tee should provide a hook 
that creates an exact replica of the (portion of the) range being 
iterated. So calling front several times is nicely out of the picture. 
The remaining tactical options are:

1. evaluate .front for the parent range once in its constructor and then 
every time right after forwarding popFront() to the parent range. This 
is a bit "eager" because the constructor evaluates .front even if the 
client never does.

2. evaluate .front for the parent range just before forwarding 
popFront() to parent. This will call front even though the client 
doesn't (which I think is fine).

3. keep a bool that is set by constructor and popFront() and reset by 
front(). The bool makes sure front() is called if and only if the client 
calls it.

I started writing the options mechanically without thinking of the 
implications. Now that I'm done, I think 2 is by far the best.

> --------
>
> I don't think "argument-less reduce" should do what you describe, as it
> would be a bit confusing what the function does. 1-names; 1-operation,
> IMO. Users might accidentally think they are getting an additive
> reduction :(

Good point.

> I think a function called "walk", in line with "walkLength", would be
> much more appropriate, and make more sense to boot!
>
> But we run into the same problem... Should "walk" call front between
> each element? Both answers are correct, IMO.

That's why I'm thinking: the moment .front gets evaluated, we get into 
the realm of reduce.


Andrei


More information about the Digitalmars-d mailing list