September 12, 2008
Steven Schveighoffer wrote:
> "Bill Baxter" wrote
>> I think one thing to consider is what it will take to make a new
>> container support and "play nice" with the regime proposed.  This
>> touches on Andrei's point about being hard pressed to think of generic
>> algorithms to run on an HMM, too.
>>
>> The first question is who do you want to "play nice" with?  If you're
>> going to be writing functions specifically for that container, then
>> you don't really have to play nice with anyone.  Your container just
>> needs to have the operations necessary to support those functions.
> 
> Bill, thanks so much for explaining it like this, I really agree with what you say.  My concern is that iterator is going to become a 'bad word' and considered a flawed design.
> 
> But you are right, there is no need for iterators to be allowed for std.algorithm, I totally agree with that, I just assumed Andrei meant iterators would be discouraged for everything, including general use as pointers into container objects.  If that is not the case, then I wholeheartedly agree that algorithms should be restricted to ranges, and iterators should be used only in container operations.

You are right. Iterators can definitely be handy in many situations, and it took me some hair pulling to figure out how to do moveToFront with ranges alone. (Then admittedly it's a pretty wicked algorithm no matter what.)

I don't want to discourage defining iterators, but rather not force you to define them when you define a new range, and also force people who want to use std.algorithm in learning them in addition to ranges.


Andrei

September 12, 2008
I like the new proposal much more than the first.

I believe you will be able to use it successfully in std.algorithm.
I still would have preferred an operation like a sameHead or compareHeadPosition (that might or might not return the order, but at least tests for equality) so that upon request (-debug flag?) one would be able to make all range operation safe (with overhead) in a generic way, but it is up to you.

I what I really care about is the following:
I want foreach magic on all objects that support .done and .next, even if they are not ranges.
foreach is about iteration, iteration needs only .done and .next (a generator, iterator whatever), and it should work with that.
Do not force the range idea on foreach iteration.
foreach is a language construct, not a library one and should allow for maximum flexibility.

As extra nicety as each generator/iterator/range returns just one object I would like to be able to do:

// i counts starting from 1, j iterates on iterJ and in parallel k iterates on a.all
foreach(i,j,k;1..$,iterJ,a.all){
	//...
}

and have it expanded to

Range!(int) r1=1..$;
alias iterJ r2;
typeof(a.all) r3=a.all;
while(!(r1.done || r2.done || r3.done)){
	typeof(r1.next) i=r1.next;
	typeof(r2.next) j=r2.next;
	typeof(r3.next) k=r3.next;
	//...
}

Fawzi

September 12, 2008
Sergey Gromov wrote:
> Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>> "Bill Baxter" wrote
>>> I think one thing to consider is what it will take to make a new
>>> container support and "play nice" with the regime proposed.  This
>>> touches on Andrei's point about being hard pressed to think of generic
>>> algorithms to run on an HMM, too.
>>>
>>> The first question is who do you want to "play nice" with?  If you're
>>> going to be writing functions specifically for that container, then
>>> you don't really have to play nice with anyone.  Your container just
>>> needs to have the operations necessary to support those functions.
>> Bill, thanks so much for explaining it like this, I really agree with what you say.  My concern is that iterator is going to become a 'bad word' and considered a flawed design.
>>
>> But you are right, there is no need for iterators to be allowed for std.algorithm, I totally agree with that, I just assumed Andrei meant iterators would be discouraged for everything, including general use as pointers into container objects.  If that is not the case, then I wholeheartedly agree that algorithms should be restricted to ranges, and iterators should be used only in container operations.
> 
> If you ask me, I think iterators AKA pointers into containers should be discouraged from SafeD.  If you don't care about SafeD you may use whatever you like.  Most library interfaces want to be SafeD to make user's life easier but few care about the library internals as long as they work.

That's also a reason why std.stdio must wrap FILE* into a safe struct. Manipulating FILE* objects directly is unsafe even if you disable pointer arithmetic.

Andrei
September 12, 2008
Fawzi Mohamed wrote:
> I like the new proposal much more than the first.
> 
> I believe you will be able to use it successfully in std.algorithm.
> I still would have preferred an operation like a sameHead or compareHeadPosition (that might or might not return the order, but at least tests for equality) so that upon request (-debug flag?) one would be able to make all range operation safe (with overhead) in a generic way, but it is up to you.

Comparing for equality of heads is very important. For now you can obtain it as a non-primitive by invoking:

auto sameHead = r.before(s).done;

The above also show how "done" is not always very expressive. Also you can compare whether two ranges have the same end by invoking:

auto sameRange = r is s;

> I what I really care about is the following:
> I want foreach magic on all objects that support .done and .next, even if they are not ranges.
> foreach is about iteration, iteration needs only .done and .next (a generator, iterator whatever), and it should work with that.
> Do not force the range idea on foreach iteration.
> foreach is a language construct, not a library one and should allow for maximum flexibility.

Yes. Walter asked me to send him the syntactic transformation that foreach and foreach_reverse need to do. Duck typing will be used so as long as you define the proper names you're in good shape.

> As extra nicety as each generator/iterator/range returns just one object I would like to be able to do:
> 
> // i counts starting from 1, j iterates on iterJ and in parallel k iterates on a.all
> foreach(i,j,k;1..$,iterJ,a.all){
>     //...
> }
> 
> and have it expanded to
> 
> Range!(int) r1=1..$;
> alias iterJ r2;
> typeof(a.all) r3=a.all;
> while(!(r1.done || r2.done || r3.done)){
>     typeof(r1.next) i=r1.next;
>     typeof(r2.next) j=r2.next;
>     typeof(r3.next) k=r3.next;
>     //...
> }

Walter and I were discussing about ranges exposing key, key1, ... keyn. In that case foreach with multiple arguments would work, and would bind each of the extra argument to key, key1 etc. respectively.


Andrei
September 12, 2008
On 2008-09-12 17:48:02 +0200, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> Fawzi Mohamed wrote:
>> I like the new proposal much more than the first.
>> 
>> I believe you will be able to use it successfully in std.algorithm.
>> I still would have preferred an operation like a sameHead or compareHeadPosition (that might or might not return the order, but at least tests for equality) so that upon request (-debug flag?) one would be able to make all range operation safe (with overhead) in a generic way, but it is up to you.
> 
> Comparing for equality of heads is very important. For now you can obtain it as a non-primitive by invoking:
> 
> auto sameHead = r.before(s).done;

nice I hadn't thought about this

> The above also show how "done" is not always very expressive. Also you can compare whether two ranges have the same end by invoking:
> 
> auto sameRange = r is s;

I suppose that you mean that "is" compares both the start and the end...

>> I what I really care about is the following:
>> I want foreach magic on all objects that support .done and .next, even if they are not ranges.
>> foreach is about iteration, iteration needs only .done and .next (a generator, iterator whatever), and it should work with that.
>> Do not force the range idea on foreach iteration.
>> foreach is a language construct, not a library one and should allow for maximum flexibility.
> 
> Yes. Walter asked me to send him the syntactic transformation that foreach and foreach_reverse need to do. Duck typing will be used so as long as you define the proper names you're in good shape.

very nice, this is important because generic algorithms aside you might want to loop on all sort of things.

>> As extra nicety as each generator/iterator/range returns just one object I would like to be able to do:
>> 
>> // i counts starting from 1, j iterates on iterJ and in parallel k iterates on a.all
>> foreach(i,j,k;1..$,iterJ,a.all){
>>     //...
>> }
>> 
>> and have it expanded to
>> 
>> Range!(int) r1=1..$;
>> alias iterJ r2;
>> typeof(a.all) r3=a.all;
>> while(!(r1.done || r2.done || r3.done)){
>>     typeof(r1.next) i=r1.next;
>>     typeof(r2.next) j=r2.next;
>>     typeof(r3.next) k=r3.next;
>>     //...
>> }
> 
> Walter and I were discussing about ranges exposing key, key1, ... keyn. In that case foreach with multiple arguments would work, and would bind each of the extra argument to key, key1 etc. respectively.

I like the possibility to give several iterators at once to foreach, so that you never have to define two opApply (one with index, one without), but you can easily add a counter if you want to.

You can solve this also by have a "combiner" of iterators, but in my opinion it is uglier.

If you allow an iterator to return several objects and also to have several iterators that are advanced together should use another syntax than the one I proposed, something like

foreach(i;1..$; j; iterJ; k,l; multiIter){

}

otherwise matching iteration variables with iterators gets a mess.

Fawzi

September 12, 2008
On Fri, 12 Sep 2008 20:10:28 +0400, Fawzi Mohamed <fmohamed@mac.com> wrote:

> On 2008-09-12 17:48:02 +0200, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
>
>> Fawzi Mohamed wrote:
>>> foreach(i,j,k;1..$,iterJ,a.all){
>>>     //...
>>> }

Foreach over multiple ranges in paraller is great, but it is quite hard to match key/value to the ranges in your example, because they are far from each other, especially if ranges are evaluated in some (possibly long) expressions.

I prefer the following syntax more:

foreach (key0, value0 : range0; value1 : range1; ... ) { // or something like this
}

This way key/value and range are close to each other and you don't need to move you look back and forth to understand what range does this value correspond too.
September 12, 2008
On Sat, Sep 13, 2008 at 3:21 AM, Denis Koroskin <2korden@gmail.com> wrote:
> On Fri, 12 Sep 2008 20:10:28 +0400, Fawzi Mohamed <fmohamed@mac.com> wrote:
>
>> On 2008-09-12 17:48:02 +0200, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
>>
>>> Fawzi Mohamed wrote:
>>>>
>>>> foreach(i,j,k;1..$,iterJ,a.all){
>>>>    //...
>>>> }
>
> Foreach over multiple ranges in paraller is great, but it is quite hard to match key/value to the ranges in your example, because they are far from each other, especially if ranges are evaluated in some (possibly long) expressions.
>
> I prefer the following syntax more:
>
> foreach (key0, value0 : range0; value1 : range1; ... ) { // or something
> like this
> }
>
> This way key/value and range are close to each other and you don't need to move you look back and forth to understand what range does this value correspond too.

Err, you just repeated exactly what he said.

--bb
September 12, 2008
On Sat, Sep 13, 2008 at 7:28 AM, Bill Baxter <wbaxter@gmail.com> wrote:
> On Sat, Sep 13, 2008 at 3:21 AM, Denis Koroskin <2korden@gmail.com> wrote:
>> On Fri, 12 Sep 2008 20:10:28 +0400, Fawzi Mohamed <fmohamed@mac.com> wrote:
>>
>>> On 2008-09-12 17:48:02 +0200, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
>>>
>>>> Fawzi Mohamed wrote:
>>>>>
>>>>> foreach(i,j,k;1..$,iterJ,a.all){
>>>>>    //...
>>>>> }
>>
>> Foreach over multiple ranges in paraller is great, but it is quite hard to match key/value to the ranges in your example, because they are far from each other, especially if ranges are evaluated in some (possibly long) expressions.
>>
>> I prefer the following syntax more:
>>
>> foreach (key0, value0 : range0; value1 : range1; ... ) { // or something
>> like this
>> }
>>
>> This way key/value and range are close to each other and you don't need to move you look back and forth to understand what range does this value correspond too.
>
> Err, you just repeated exactly what he said.

Ok sorry I do see a difference now, but you quoted the wrong one of Fawzi's,  you should have quoted this one:

foreach(i;1..$; j; iterJ; k,l; multiIter){

}

Which I think falls into your "or something like this" category.

--bb
September 25, 2008
Andrei Alexandrescu wrote:
>I'd also like *r as a shortcut for r.first, 

Agh, yuck! :(


-- 
Bruno Medeiros - Software Developer, MSc. in CS/E graduate
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 25, 2008
Andrei Alexandrescu wrote:
> 
> This is because I make next to no money so I can afford to work on basic research, which is "important" in a long-ranging way. Today's computing is quite disorganized and great energy is expended on gluing together various pieces, protocols, and interfaces. I've worked in that environment quite a lot, and dealing with glue can easily become 90% of a day's work, leaving only little time to get occupied with a real problem, such as making a computer genuinely smarter or at least more helpful towards its user. All too often we put a few widgets on a window and the actual logic driving those buttons - the "smarts", the actual "work" gets drowned by details taking care of making that logic stick to the buttons.
> 

Well, didn't you find a "real problem" right there (and also a very interesting one), in trying to make code/libraries/methodologies/tools/whatever that reduce those 90% of work in boilerplate details?
An example could the years of investment and research in ORM frameworks (Hibernate/EJB3, Ruby on Rails, etc.), which despite ORM technology having existed for quite many years, only recently has it reached a point where it's really easy and non-tedious to write an OO-DB persistence mapping.
Another possible example, regarding GUI programming like you mentioned, is data binding. I haven't used it myself yet, but for what they describe, it's purpose is indeed to reduce a lot of the complexity and tedium in writing code to synchronize the UI with the model/logic, and vice-versa.
Learning and building these kinds of stuff is, IMO, the pinnacle of software engineering.

-- 
Bruno Medeiros - Software Developer, MSc. in CS/E graduate
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D