July 12, 2010
On 07/12/2010 01:47 PM, Steven Schveighoffer wrote:
> On Mon, 12 Jul 2010 13:49:50 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>> Yes. The point is that with a delegate you must choose between
>> accepting E and E[]. Given the constraint, it's better for everyone to
>> accept E[] and let put() take care of the occasional E by doing the
>> wraparoo (&elem)[0..1].
>
> But given a delegate that takes a single element, there's no way to wrap
> it so it can be an output range. Yet such a delegate can easily be
> something that outputs something.

void delegate(int) perItem;
void ofCourseThereIsAWay(int[] items) {
    foreach (e; items) perItem(i);
}

> Indeed, a delegate that takes a string takes a single element, but
> because a string happens to be defined as a range of chars, it passes
> the test for output ranges.

Yah, that's a good point.

> I could loop on an array of strings of one character, and output that to
> a valid output range no problem. The only thing that solves this problem
> correctly is buffering.

I don't understand the point here.

> What if I have my own container types that are large chunks of data, but
> don't happen to define the input range primitives? Why should I be
> artificially prevented from using those as input to output ranges?

I don't understand this either.

> Really to me, you are saying, "I want your delegate to be efficient",
> but you defined something that is related to that in a small set of
> circumstances (when the arrays being passed in are large).

What I'm saying is, "If you know how to output one item you may as well output several, as I'm producing them in bulk".

> Here's a proposal for put/isOutputRange which would solve my problem and
> not have any for loops in it:
[snip]

I'm uncomfortable about allowing inefficiency by design if I can help it, but I guess the costs all depend on the costs of trafficking one item.

>>> I can tell you this for sure, because it's exactly what's in many
>>> dcollections classes.
>>>
>>> So what happens when you call put(r, e) for one of these output classes?
>>> Instead of just calling add(e), it calls (add((&e)[0..1])) which in turn
>>> goes through some needless loop, which then ends up calling add(e). I
>>> don't see why this is preferable.
>>
>> Ah, I see. There is a confusion. The array restriction is only for
>> delegates. For straight ranges, you should accept individual Es.
>
> Why the discrepency? A naive coder can define inefficient ranges just as
> well as he can define inefficient delegates.

That doesn't mean we shouldn't foster the mechanisms that reduce the chance of bad designs happening.


Andrei
July 12, 2010
On Mon, 12 Jul 2010 15:18:17 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 07/12/2010 01:47 PM, Steven Schveighoffer wrote:
>> On Mon, 12 Jul 2010 13:49:50 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>> Yes. The point is that with a delegate you must choose between
>>> accepting E and E[]. Given the constraint, it's better for everyone to
>>> accept E[] and let put() take care of the occasional E by doing the
>>> wraparoo (&elem)[0..1].
>>
>> But given a delegate that takes a single element, there's no way to wrap
>> it so it can be an output range. Yet such a delegate can easily be
>> something that outputs something.
>
> void delegate(int) perItem;
> void ofCourseThereIsAWay(int[] items) {
>      foreach (e; items) perItem(i);
> }

Yeah, I realized after posting that it was incorrect :)  But encouraging this kind of thing is probably not fostering efficient code (roll your own delegate when the compiler complains).  It goes to my example with dcollections and add.

>> I could loop on an array of strings of one character, and output that to
>> a valid output range no problem. The only thing that solves this problem
>> correctly is buffering.
>
> I don't understand the point here.

My point is, if the reason behind requiring arrays instead of single elements is to force efficiency, then the reason is flawed.  I can make inefficient code even when having to pass arrays to an output range.

To solve this, you could make an output range that buffers elements until it has enough to pass to an underlying sink that takes an array of elements.  The point is, you've only gone halfway to ensuring efficiency.  But going the full way means you are imposing possibly bad buffering semantics on everything.  If you can only go halfway, then I think the design is more annoying than successful.

>
>> What if I have my own container types that are large chunks of data, but
>> don't happen to define the input range primitives? Why should I be
>> artificially prevented from using those as input to output ranges?
>
> I don't understand this either.

Well, I had started constructing an example of how this would work, but I realize, I have no idea how you intend to use such a delegate as an output range...  I don't really know how such output ranges will be generically usable in conjunction with output ranges which support the put interface.

>
>> Really to me, you are saying, "I want your delegate to be efficient",
>> but you defined something that is related to that in a small set of
>> circumstances (when the arrays being passed in are large).
>
> What I'm saying is, "If you know how to output one item you may as well output several, as I'm producing them in bulk".

I have no control over being able to output items in bulk or not, all I have is a delegate.  What do I do?

>> Here's a proposal for put/isOutputRange which would solve my problem and
>> not have any for loops in it:
> [snip]
>
> I'm uncomfortable about allowing inefficiency by design if I can help it, but I guess the costs all depend on the costs of trafficking one item.

I'm unsure how it will work either.  I admit now that I didn't think through how this will be used.  I imagined that the delegate version would be substituted for an output range which takes an element at a time, but now I'm not sure how you could write generic code that does that, given that the delegate must take an array of elements instead.  I guess I'll wait to see how it works before objecting any further.

-Steve
July 12, 2010
On 2010-07-12 13:49:50 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> The point is that with a delegate you must choose between accepting E and E[]. Given the constraint, it's better for everyone to accept E[] and let put() take care of the occasional E by doing the wraparoo (&elem)[0..1].

If this means what I think, it means put() cannot be memory safe. Making an array from a stack variable and passing it around cannot be safe unless you can trust this reference won't escape the scope of the delegate you're calling (and there's no way to enforce that for dynamic arrays). To be safe, all you can do is copy the element on the heap. Am I wrong?

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

July 12, 2010
On 12/07/2010 03:17, Andrei Alexandrescu wrote:
> he notion of output range has been a tad vague in the past; up until now
> a range that wanted to qualify as an output range had to define a method
> called put.

What if we have to deal with non orthogonal structures, or .. simple directed graphs ?

well well.. yet we do not even have a simple map!(string , T)  in std.container .. So, For me the Range Stuff still lacks proof of product. I remain pretty sceptical.
bjoern
July 12, 2010
On 07/12/2010 02:41 PM, Steven Schveighoffer wrote:
> I'm unsure how it will work either. I admit now that I didn't think
> through how this will be used.

It's very simple. As far as a user of an output range is concerned, they should write stuff like:

put(r, '[');
char[] someBuf;
put(r, someBuf);
put(r, ", ");
put(r, ']');

in confidence that things are reasonably efficient.


Andrei
July 12, 2010
On Mon, 12 Jul 2010 17:25:43 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 07/12/2010 02:41 PM, Steven Schveighoffer wrote:
>> I'm unsure how it will work either. I admit now that I didn't think
>> through how this will be used.
>
> It's very simple. As far as a user of an output range is concerned, they should write stuff like:
>
> put(r, '[');
> char[] someBuf;
> put(r, someBuf);
> put(r, ", ");
> put(r, ']');
>
> in confidence that things are reasonably efficient.

How does that work for a range whose front() can be assigned a dchar?  Wait, it doesn't, because it won't compile.

But wouldn't that be the same for a delegate that takes a dchar?

I'm very confused at what you are trying to do.  I expected that a char[] would be a valid output range.

-Steve
July 12, 2010
On 07/12/2010 04:39 PM, Steven Schveighoffer wrote:
> On Mon, 12 Jul 2010 17:25:43 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 07/12/2010 02:41 PM, Steven Schveighoffer wrote:
>>> I'm unsure how it will work either. I admit now that I didn't think
>>> through how this will be used.
>>
>> It's very simple. As far as a user of an output range is concerned,
>> they should write stuff like:
>>
>> put(r, '[');
>> char[] someBuf;
>> put(r, someBuf);
>> put(r, ", ");
>> put(r, ']');
>>
>> in confidence that things are reasonably efficient.
>
> How does that work for a range whose front() can be assigned a dchar?
> Wait, it doesn't, because it won't compile.
>
> But wouldn't that be the same for a delegate that takes a dchar?
>
> I'm very confused at what you are trying to do. I expected that a char[]
> would be a valid output range.

A char[] should be a valid output range for a dchar. I forgot to encode all valid strings situations, working on that now.

Andrei
July 13, 2010
On 07/12/2010 04:39 PM, Steven Schveighoffer wrote:
> On Mon, 12 Jul 2010 17:25:43 -0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 07/12/2010 02:41 PM, Steven Schveighoffer wrote:
>>> I'm unsure how it will work either. I admit now that I didn't think
>>> through how this will be used.
>>
>> It's very simple. As far as a user of an output range is concerned,
>> they should write stuff like:
>>
>> put(r, '[');
>> char[] someBuf;
>> put(r, someBuf);
>> put(r, ", ");
>> put(r, ']');
>>
>> in confidence that things are reasonably efficient.
>
> How does that work for a range whose front() can be assigned a dchar?
> Wait, it doesn't, because it won't compile.
>
> But wouldn't that be the same for a delegate that takes a dchar?
>
> I'm very confused at what you are trying to do. I expected that a char[]
> would be a valid output range.

Actually a char[] is not a valid output range. Overwriting variable-length codes with other variable-length codes might mess up the string.

Here's what I have. Works?

void put(R, E)(ref R r, E e)
{
    static if (!isArray!R && is(typeof(r.put(e))))
    {
        r.put(e);
    }
    else static if (!isArray!R && is(typeof(r.put((&e)[0..1]))))
    {
        r.put((&e)[0..1]);
    }
    else static if (is(typeof(r.front = e, r.popFront())))
    {
        r.front = e;
        r.popFront();
    }
    else static if (isInputRange!E && is(typeof(put(r, e.front))))
    {
        for (; !e.empty; e.popFront()) put(r, e.front);
    }
    else static if (is(typeof(r(e))))
    {
        r(e);
    }
    else static if (is(typeof(r((&e)[0..1]))))
    {
        r((&e)[0..1]);
    }
    else
    {
        static assert(false, "Cannot put a "~E.stringof~" into a "~R.stringof);
    }
}


Andrei
July 13, 2010
On 07/12/2010 09:58 PM, Andrei Alexandrescu wrote:
> Here's what I have. Works?
[snip]

To clarify:

http://erdani.com/d/phobos/std_range.html

Andrei
July 13, 2010
On Tue, Jul 13, 2010 at 05:18, Andrei Alexandrescu < SeeWebsiteForEmail@erdani.org>  wrote:

    To clarify:

    http://erdani.com/d/phobos/std_range.html


Seems good to me. Lots of flexibility. I may begin to write output ranges :-)

So in the very first case, e may well be a range, but the way it's used internally is up to the output range designer. Maybe it uses a loop, maybe not. OK.

You call r(e) and r([e])) 'delegates'. Are you just using it as a generic
term, or does that mean using any callable (struct with opCall) is not a
good idea in this case, for whatever reason?


Philippe