May 20, 2009
== Quote from Bill Baxter (wbaxter@gmail.com)'s article
> On Wed, May 20, 2009 at 11:44 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> > Jason House wrote:
> >>
> >> Andrei Alexandrescu Wrote:
> >>
> >>> Jason House wrote:
> >>>>
> >>>> I feel like there are too many differences between input and forward ranges for such a minor difference. Many range functions are written assuming no side effects on the caller. This can restrict the use of helper functions. It may be best to make their usage different...
> >>>
> >>> So how do you think we should go about it? Also don't forget that any ranges should be seamlessly and efficiently treated as input ranges.
> >>>
> >>> Andrei
> >>
> >> You won't like my answer!
> >>
> >> Like you've already said, the semantics of forward ranges and input ranges are different. I would argue that forward ranges have value semantics but input ranges do not. Any function that implicitly assumes value semantics is wrong. Sadly, overlapping API's makes that all too easy for someone to write bad code that passes simplistic tests with forward ranges and then fail with input ranges.
> >>
> >> My initial thoughts is that input ranges should have two changes:
> >> 1. A different API from forward ranges
> >> 2. Be a reference type (AKA class instead of struct)
> >>
> >> In short, I disagree with your basic premise of treating the two ranges similarly.
> >
> > I don't want to treat them similarly, but we should be able to treat forward ranges as input ranges. Otherwise, all algorithms that work for input ranges would have to be written twice.
> auto inp = std.typecons.inputRangeFromForwardRange(fwd);
> No need to write the algos twice now, but you do have to add a line or
> two of code to every input range algo.  Or force the the user to call
> the converter function.
> --bb

On a broader note, I think that people need to understand that, just as a free society can never be a perfectly safe society, a language that allows programmers the freedom to create concise, general and efficient constructs can never be a perfectly safe language.  Yes, we could make D as safe as Java, but then programming in D would be like programming in Java--an exercise in superfluous verbosity and getting around the language's rigidity.
May 20, 2009
On Wed, 20 May 2009 14:02:02 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Robert Jacques wrote:
>> Bicycle shed: Well, since output ranges use 'put', how about 'get' for input ranges?
>
> Nice color :o). In fact, "put" is a poor choice because it doesn't reflect advancement. Probably putNext and getNext are better.
>
> Andrei

Well, on that note, more shed colors and common use cases:
          stacks, queues, messge passing, file I/O, network I/O, confusion factor
send/recv  weak ,  okay ,      good     ,  weak   ,    good    ,    low
sink/rise  bad  ,  bad  ,      bad      ,  bad    ,    bad     ,    high
push/pop   good ,  okay ,      okay     ,  okay   ,    okay    ,    high
May 20, 2009
On Wed, May 20, 2009 at 12:05 PM, dsimcha <dsimcha@yahoo.com> wrote:
> == Quote from Bill Baxter (wbaxter@gmail.com)'s article
>> On Wed, May 20, 2009 at 11:44 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>> > Jason House wrote:
>> >>
>> >> Andrei Alexandrescu Wrote:
>> >>
>> >>> Jason House wrote:
>> >>>>
>> >>>> I feel like there are too many differences between input and forward ranges for such a minor difference. Many range functions are written assuming no side effects on the caller. This can restrict the use of helper functions. It may be best to make their usage different...
>> >>>
>> >>> So how do you think we should go about it? Also don't forget that any ranges should be seamlessly and efficiently treated as input ranges.
>> >>>
>> >>> Andrei
>> >>
>> >> You won't like my answer!
>> >>
>> >> Like you've already said, the semantics of forward ranges and input ranges are different. I would argue that forward ranges have value semantics but input ranges do not. Any function that implicitly assumes value semantics is wrong. Sadly, overlapping API's makes that all too easy for someone to write bad code that passes simplistic tests with forward ranges and then fail with input ranges.
>> >>
>> >> My initial thoughts is that input ranges should have two changes:
>> >> 1. A different API from forward ranges
>> >> 2. Be a reference type (AKA class instead of struct)
>> >>
>> >> In short, I disagree with your basic premise of treating the two ranges similarly.
>> >
>> > I don't want to treat them similarly, but we should be able to treat forward ranges as input ranges. Otherwise, all algorithms that work for input ranges would have to be written twice.
>> auto inp = std.typecons.inputRangeFromForwardRange(fwd);
>> No need to write the algos twice now, but you do have to add a line or
>> two of code to every input range algo.  Or force the the user to call
>> the converter function.
>> --bb
>
> But if you make the input range a class as Jason proposed, then:
>
> 1.  Unless it's final, its methods will be virtual (slow).
> 2.  You trigger a heap allocation every time you want to make this conversion.  (slow)

Maybe, but I don't really agree that input ranges should be forced to be classes.  Seems like they should be allowed to be either as long as they support the required methods.

Actually that's a good argument for not making  a = b part of the Forward Range concept.   If you get rid of that one, then Forward Ranges can be either classes or structs too.

--bb
May 20, 2009
On Wed, 20 May 2009 21:02:02 +0300, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Robert Jacques wrote:
>> Bicycle shed: Well, since output ranges use 'put', how about 'get' for input ranges?
>
> Nice color :o). In fact, "put" is a poor choice because it doesn't reflect advancement. Probably putNext and getNext are better.
>
> Andrei

(Just thinking aloud... :) ) I have been using get() + set() and read() + write().
read() and write() advance to the next item; get() + set() do not.

Actually I have implemented my iterator classes (in C++) as follows (simplified):

BasicIFlow:
read()
toNext()
isEnd()

IFlow:
get()
read()
toNext()
isEnd()

Iter:
get()
read()
toNext()
toPrev()
isBegin()
isFirst()
isLast()
isEnd()

As seen, Iter is a two-way iterator, and the other two are one-way iterators. (And there are correponding output iterators too, of course.)

There are also functions like toFirst(), toEnd(), etc (only Iter has toFirst()). And for convenience, Iter also has functions like getNext() and getPrev() that return the next/previous item without moving the iterator. So there are quite many functions, which is not necessary good ;) (although many of them have default functionality that simply calls the other "core" functions; for example, read() *could* be written with get() + toNext()).

I know very little about Ranges (when I have time, that will change), but if I'm not mistaken, they hold and modify the beginning and end of the iterated area? That's an interesting and unique approach. :) My classes move a cursor inside the iterated area. Of course, with the Flow classes, the beginning of the area is moved together with the cursor (as the cursor cannot move backwards).
May 20, 2009
dsimcha Wrote:

> == Quote from Bill Baxter (wbaxter@gmail.com)'s article
> > On Wed, May 20, 2009 at 11:44 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> > > Jason House wrote:
> > >>
> > >> Andrei Alexandrescu Wrote:
> > >>
> > >>> Jason House wrote:
> > >>>>
> > >>>> I feel like there are too many differences between input and forward ranges for such a minor difference. Many range functions are written assuming no side effects on the caller. This can restrict the use of helper functions. It may be best to make their usage different...
> > >>>
> > >>> So how do you think we should go about it? Also don't forget that any ranges should be seamlessly and efficiently treated as input ranges.
> > >>>
> > >>> Andrei
> > >>
> > >> You won't like my answer!
> > >>
> > >> Like you've already said, the semantics of forward ranges and input ranges are different. I would argue that forward ranges have value semantics but input ranges do not. Any function that implicitly assumes value semantics is wrong. Sadly, overlapping API's makes that all too easy for someone to write bad code that passes simplistic tests with forward ranges and then fail with input ranges.
> > >>
> > >> My initial thoughts is that input ranges should have two changes:
> > >> 1. A different API from forward ranges
> > >> 2. Be a reference type (AKA class instead of struct)
> > >>
> > >> In short, I disagree with your basic premise of treating the two ranges similarly.
> > >
> > > I don't want to treat them similarly, but we should be able to treat forward ranges as input ranges. Otherwise, all algorithms that work for input ranges would have to be written twice.
> > auto inp = std.typecons.inputRangeFromForwardRange(fwd);
> > No need to write the algos twice now, but you do have to add a line or
> > two of code to every input range algo.  Or force the the user to call
> > the converter function.
> > --bb
> 
> But if you make the input range a class as Jason proposed, then:
> 
> 1.  Unless it's final, its methods will be virtual (slow).
> 2.  You trigger a heap allocation every time you want to make this conversion.  (slow)

Scope classes avoid the heap allocations. Classes are not required for referance semantics. Specially constructed structs can also satisfy the requirement. By declaring the typical input range to be a (scope final) class, I was hoping to emphasize the fundamental difference with forward ranges.

It should be trivial to write a (scope) wrapper that converts a forward range into an input range. The compiler should be able to optimize away the wrapper or at least inline the functions.
May 20, 2009
Bill Baxter Wrote:

> On Wed, May 20, 2009 at 12:05 PM, dsimcha <dsimcha@yahoo.com> wrote:
> > == Quote from Bill Baxter (wbaxter@gmail.com)'s article
> >> On Wed, May 20, 2009 at 11:44 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> >> > Jason House wrote:
> >> >>
> >> >> Andrei Alexandrescu Wrote:
> >> >>
> >> >>> Jason House wrote:
> >> >>>>
> >> >>>> I feel like there are too many differences between input and forward ranges for such a minor difference. Many range functions are written assuming no side effects on the caller. This can restrict the use of helper functions. It may be best to make their usage different...
> >> >>>
> >> >>> So how do you think we should go about it? Also don't forget that any ranges should be seamlessly and efficiently treated as input ranges.
> >> >>>
> >> >>> Andrei
> >> >>
> >> >> You won't like my answer!
> >> >>
> >> >> Like you've already said, the semantics of forward ranges and input ranges are different. I would argue that forward ranges have value semantics but input ranges do not. Any function that implicitly assumes value semantics is wrong. Sadly, overlapping API's makes that all too easy for someone to write bad code that passes simplistic tests with forward ranges and then fail with input ranges.
> >> >>
> >> >> My initial thoughts is that input ranges should have two changes:
> >> >> 1. A different API from forward ranges
> >> >> 2. Be a reference type (AKA class instead of struct)
> >> >>
> >> >> In short, I disagree with your basic premise of treating the two ranges similarly.
> >> >
> >> > I don't want to treat them similarly, but we should be able to treat forward ranges as input ranges. Otherwise, all algorithms that work for input ranges would have to be written twice.
> >> auto inp = std.typecons.inputRangeFromForwardRange(fwd);
> >> No need to write the algos twice now, but you do have to add a line or
> >> two of code to every input range algo.  Or force the the user to call
> >> the converter function.
> >> --bb
> >
> > But if you make the input range a class as Jason proposed, then:
> >
> > 1.  Unless it's final, its methods will be virtual (slow).
> > 2.  You trigger a heap allocation every time you want to make this conversion.  (slow)
> 
> Maybe, but I don't really agree that input ranges should be forced to be classes.  Seems like they should be allowed to be either as long as they support the required methods.

If you really mean methods and semantics, then I agree. It's becoming increasingly clear to me that D users struggle against the struct/class division. I frequently think of the division as value/reference type while many others think of it as non-virtual/virtual. These different perspectives means there is a large set of "objects" where the two definitions disagree on which data type (struct or class) is more appropriate. IMHO, D should have a type with low size and function call overhead like a struct as well as reference semantics like a class.

> Actually that's a good argument for not making  a = b part of the Forward Range concept.   If you get rid of that one, then Forward Ranges can be either classes or structs too.

Having undefined behavior (for assignments and passing as arguments) is bad.
May 20, 2009
On Wed, 20 May 2009 13:35:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Jason House wrote:
>> I feel like there are too many differences between input and forward
>> ranges for such a minor difference. Many range functions are written
>> assuming no side effects on the caller. This can restrict the use of
>> helper functions. It may be best to make their usage different...
>
> So how do you think we should go about it? Also don't forget that any ranges should be seamlessly and efficiently treated as input ranges.
>
> Andrei

struct Input(R) // enforce that R is a forward range for type T, not versed well enough in D2 to know how to do this yet
{
  R _range; // make this R * if you want Input(R) to be a reference type
  alias _range this;
  auto popNext() { auto result = _range.front; _range.popFront(); return result; }
  // and so-on, can leave out truly duplicate functions like empty as the alias this will take care of that.
}

// convenience method
Input!(R) asInput(R)(R r)
{
  return Input!(R)(r); // or Input!(R)(new R(r)); if reference type is the right answer
}

No extra code required in any forward range, just wrap it.  Input still retains the forward range functions as an underlying base if you need BOTH input and forward range functionality (not sure why).

-Steve
May 20, 2009
== Quote from Jason House (jason.james.house@gmail.com)'s article
> IMHO, D should have a type with low size and function call overhead > like a
struct as well as reference semantics like a class.

What's wrong with a pointer to a heap-allocated struct?  I sometimes need what you describe, too, and I've never seen a case where this doesn't do the job.
May 20, 2009
On Wed, May 20, 2009 at 4:03 PM, dsimcha <dsimcha@yahoo.com> wrote:
> == Quote from Jason House (jason.james.house@gmail.com)'s article
>> IMHO, D should have a type with low size and function call overhead > like a
> struct as well as reference semantics like a class.
>
> What's wrong with a pointer to a heap-allocated struct?  I sometimes need what you describe, too, and I've never seen a case where this doesn't do the job.

And you can alias Foo_* Foo, so that it doesn't even look like you're passing around a pointer.  :-)


--bb
May 21, 2009
dsimcha Wrote:

> == Quote from Jason House (jason.james.house@gmail.com)'s article
> > IMHO, D should have a type with low size and function call overhead > like a
> struct as well as reference semantics like a class.
> 
> What's wrong with a pointer to a heap-allocated struct?  I sometimes need what you describe, too, and I've never seen a case where this doesn't do the job.

That does the job, but it looks ugly ;) I think it's also not allowed in safe d.