473,395 Members | 1,458 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

foreach enhancement

What about an enhancement of foreach loops which allows a syntax like that:

foeach(int i in 1..10) { } // forward
foeach(int i in 99..2) { } // backwards
foeach(char c in 'a'..'z') { } // chars
foeach(Color c in Red..Blue) { } // using enums

It should work with all integral datatypes. Maybe we can step a bit further:

foeach(int i in 1..10, 30..100) { } // from 1 to 10 and 30 to hundred

And maybe we could also try to enhance the loop further and make it usable
for floating point types too:

foeach(float f in 1.0..9.5 : 0.5) { } // from 1.0 to 9.5 in steps of 0.5

This is far more readable as a for loop and makes the intend much clearer.
I always have to think if I write loops like "for (int i=list.Count-1; i<=0;
i--)".
This is unproductive and errorprone and hard to maintain. Thus, my proposal.

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05
104 7025

"cody" <pl*************************@gmx.de> wrote in message
news:%2****************@TK2MSFTNGP11.phx.gbl...
3b) For arrays all values have to be of the same type. Array literals
take
on the numeric behaviour where the largest nessecery type is used, but
arrays cannot contain multiple types.
We also could say that for objects we make the array of a type that is
common base class of *all* of the array members and supports all
interfaces
which *all* objects in the array support:

{string, object} // array type is object
{IList, Array} // array type if object supporting IList
{Control, TextBox} // array type is Control


sounds right.
If the array contains value types *and* reference types, the type of the
array is object.
If only numeric types are involved I agree that the array should be of the
type of the biggest nessecary numeric type.
4) For enumerableExpression, every value is read out *unless* the expression
is cast to object or somesuch. This does leave a problem in that there is

no
way to generate a list of IEnumerables, but if someone wants that edge

case
they should probably use long-hand list or array syntax. Can you think of
any workarounds for this?
enumerableExpression is typed either object for IEnumerable or T for
IEnumerable<T>.


I do not like that Idea. It would mean that you cannot put any object
supporting IEnumerable into the array without splitting it up into its
items. Better would be IEnumerator instead of IEnumerable, which means
you'll have to put list.GetEnumerator() into the array so it'll be
splitted
up. But I still do not like that idea. Array have a fixed size and
allowing
IEnumerators to be inserted into array literals means that during runtime
you have to create temporary lists and copy from them into the destination
array since you cannot resize it.
e.g:

{obj1, obj2, list1}

Assumed you want to guarantee that the expressions are evaluated in the
order they appear in the declaration you have first to create a temporary
list. you put obj1 and obj2 in it. then you have to evaluate list1 and add
its contents to the list. after you've evaluated all objects you can call
.ToArray() and your array is constructed.
But again, I do not like the idea of expanding IEnumerables. If I declare
an
array literal containg 3 elements I expect that is really has 3 elements
and
not more.

The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without breaking
the rules of the language is pretty tough. I've been considering a new
operator, <-, which assigns each value in an enumerable object to a variable
5) sourceExpression is any expression that returns a type, be it
mathematical or a method call. These aren't literals in a very strict sense,
every one of these values is generated at run time(unless compiletime
generation is possible).


agreed.
6) Where possible, foreach over a list or generator will generate a for
loop. This will only occur if the list generates integers or other
structures that support > <, etc. If the compiler cannot determine a
valid
for loop or if code complexity would become to great(too many ranges or
multiple types), the compiler will instead generate the list and foreach
will operate over that lists IEnumerable implementation.


You are talking about set literals now? We should clearly separate the
short
array literal syntax and the multi-range-set-literal syntax, they have
nothing to do with each other.


I'm talking optimization of range literals. For the case foreach (int i in
1...1000) I see no reason to generate an object, instead a for loop like you
suggested is generatable. However, for code like
foreach(int i in 1...1000, 1000...1, 2...5 : .5, 3...5838) a generator
object would probably make more sense as it would *vastly* simplify the
output code and probably the break statement as well.
8) For contains checking, I'm still much more comfortable with isin over

in,
so I will likely implement that keyword as it stands. However I will
consider adding a pragma that changes support to in so that eitehr can be
experimented with.


I do not agree but you are the one who implements it :)


Yeah, there is a little bit of a boon to that sometimes, ;).
9) Experimental: If code contains the same range type twice only one
IEnumerable\IEnumerator class pair will be generated. That pair will take
parameters that determine ranges. This should cut down on class bloat
Good idea. The bad thing is that generics do not support operator
overloading which means we cannot use generics here and we must create a
new
class for each type.

However, I still think that it is still possible to implement this
sets/ranges without the creation of any classes/objects. It will maybe
make
the implemention harder but will make the code faster and do not need
temporary objects and a bloat of classes.

"if (a in [0..100, 200..300])" should only be a substitute of
"if ((a>=0&&a<=100)||(a>=200&&a<=300))",

and "foreach (a in 0..100, 200,300)" should simply be compiled to
"for (int a=0;a<=100;a++){} for (int a=200;a<=300;a++){}"

But this is only implementation detail, no programmer using the language
should be affected on how it is implemented.
A simply implementation as a proof of concept should be enough for now and
hopefully our ideas will make it into regular c# someday :)


In many situations, the shortcuts will work(and I will use them wehre
possible). However, as a underlying foundation each form of syntax does
return a value and exists as an expression unto itself:
1...1000 is "A sequence that runs from the number one to the number 1000,
inclusive" and is encapsulated in an IEnumerable(I'm not using IEnumerator
due to IEnumerator<T> not supporting Reset. to have a reusable set you need
to be able to reset the resultant enumerator, thus GetEnumerator must be
accessible).
[1...1000] is "A list generated from a enumerable sequence"
{1...1000} is "An array generated from an enumerable sequence".

As such, each can stand alone. The stand alone objects can then be used in
other situations, vastly expanding (potential) usage.

Since this is a proof-of-concept implementation, I am also looking at
slightly more complicated subjects like haskell&python like list
comprehensions(which I think effectivly become mini-iterators). My existing
implementatino ideas are too unpolished to post right now(6 pages of
rambling is to much), but the current syntax allows you to do things like
sequence unions and intersections, value mutation, value analysis, etc.
some rough samples:
[1...1000 where value%2 == 0] //returns all even numbers between 1 &1000,
inclusive
[yield Math.Pow(value,2) for 1...1000 where Math.Pow(value,2)%2 == 0]
//returns the square of each value which has an even square

But, as I said these are *very* rough and I'm not sure how or if I'll
bother. I do like the concept though and they are quite popular in python.

I'll post more on syntax reasoning if anyone is interested. --
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk

Nov 16 '05 #51
, every value is read out *unless* the
expression
is cast to object or somesuch. This does leave a problem in that there
is

no
way to generate a list of IEnumerables, but if someone wants that edge

case
they should probably use long-hand list or array syntax. Can you think
of
any workarounds for this?
enumerableExpression is typed either object for IEnumerable or T for
IEnumerable<T>.


I do not like that Idea. It would mean that you cannot put any object
supporting IEnumerable into the array without splitting it up into its
items. Better would be IEnumerator instead of IEnumerable, which means
you'll have to put list.GetEnumerator() into the array so it'll be
splitted
up. But I still do not like that idea. Array have a fixed size and
allowing
IEnumerators to be inserted into array literals means that during runtime
you have to create temporary lists and copy from them into the
destination
array since you cannot resize it.
e.g:

{obj1, obj2, list1}

Assumed you want to guarantee that the expressions are evaluated in the
order they appear in the declaration you have first to create a temporary
list. you put obj1 and obj2 in it. then you have to evaluate list1 and
add
its contents to the list. after you've evaluated all objects you can call
.ToArray() and your array is constructed.
But again, I do not like the idea of expanding IEnumerables. If I declare
an
array literal containg 3 elements I expect that is really has 3 elements
and
not more.

The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without
breaking the rules of the language is pretty tough. I've been considering
a new operator, <-, which assigns each value in an enumerable object to a
variable

Err, I didn't finish that.

I've been considering it but Ican't find a clean way to add it. Adding
enumerable objects contents to lists and arrays is vital, and it is vital to
do with with IEnumerable. Remember that there is *no* magic in a compiler,
atleast not in a C based language compiler. Everything has to work out in
the end.

How do you express one without the other?
Nov 16 '05 #52

"Michael C" <mi*******@optonline.net> wrote in message
news:ON**************@TK2MSFTNGP11.phx.gbl...

"cody" <pl*************************@gmx.de> wrote in message
news:ef**************@tk2msftngp13.phx.gbl...

This would all certainly be possible, without any problem. Such a
sequence
class can be written in half an hour, but that is not the point here. The
idea was to invent a new language feature which allow the programmer to loop
over multiple ranges and test wheather a value exists in a range with a
simple built in syntax and *without* creating any temporary objects.


Ahh, I thought the point was to be able to create sequences that would
allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.


As cody said, the original goal was to make for's easier to write, in
esscense. Its not quite the same because you can't manually advance i, but
still, that was the goal.
The goal grew, for me anyway, to permit the language to succinctly express
lists and some list operations without using explict classes. Much of what I
want to see isn't expressly possible with existing syntax and having to
write a method to do it for each situation is overkill(one method is 10
minutes maybe, but if you have to do it a hundred times a month?). A good
deal of what I'm looking at and driving towards is python\haskell like lists
and list comprehensions.

I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order
to
accomplish something that can be done in half an hour.
Language syntax extension isn't the only acceptable solution(although it is
for the original concept). However I don't think there is any harm in
exploring new syntax that would ease certain concepts in the language. After
all, there are quite a few things you don't *have* to have in the language
that are quite useful, like using, foreach, lock, switch, break, continue,
goto, else, etc. They make the language easier even though you could use
other constructs to achieve the same effective result if not the exact code.

Part of the purpose of these implementations is to suggest ideas and examine
them by using them. This allows you to really get an idea of how the code
would look and work in real scenarios. I've written a number of
modifications to the language, some were throwaway, some were 'just seeing
if I can', and others where things I would really like to see change in the
language. Its a hobby and an excercise, and maybe one of my ideas may filter
up and actually affect change. I don't think there is anything really wrong
with that, as long as the idea is a good one and is generally considered a
good one.

Considering I am going to implement this, I obviously support it. However, I
would and do appreciate any comments you have on the syntax as it stands(as
explained in other parts of this thread) as well as on its actual nessecity,
even if they are negative. For what its worth, I do happen to agree with you
that the syntax isn't absolutly vital, as everything I have planned can be
packaged up into a class(with anonymous methods list comprehensions), I just
think that readability, ease of authoring, and the optimization potential
warrents atleast implementing and using the syntax for a while. Hopefully an
implementation wil atleast spark a little more discussion about its merits
and bring in some ground support and clearly inform us that the community as
a whole doesn't like it(as opposed to simply not bothering getting into the
discussion).
Best of luck with that.

Thanks,
Michael C.

Nov 16 '05 #53

"cody" <pl*************************@gmx.de> wrote in message
news:OW**************@TK2MSFTNGP12.phx.gbl...
Ahh, I thought the point was to be able to create sequences that would allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.


Indeed, that was the point.
I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order

to
accomplish something that can be done in half an hour.


You'll agree, that foreach (a in 1..100, 200..300) is far better than

foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))

don't you? And exactly that is the purpose of my proposal.
Loops are one of the most common used things and making them more readable
and maintainable should be a desirable goal, don't you agree?


'Better' is a subjective term; and I actually don't agree that it's
'better'. BTW, I'm sure you haven't noticed, but I've been with this thread
from the beginning. In fact, I was the first poster to defend your general
ideas about improving the quality of life for programmers after your ideas
were initially blasted by other posters. That notwithstanding, I have a
couple of questions for you:

I have a sequence 1..100. Is it possible to create a dynamically generated
sequence at run-time using your method like this:

int x = 1;
int y = 100;
foreach (a in x..y)

Now, assuming I want to specifically exclude 10..20 or 30..40 based on a
user response at run-time, how do I accomplish this using ..?

Now let's say that I want to create a second range at run-time. How can I
do this using ..?

With this second range, expanding on the example above; let's say it's
75..125. Now I want my loop to cover the combination of the two ranges, as
opposed to the single range. How do I do this? I don't want to create a
second foreach loop with a conditional statement attached to do it.

Let's say that I don't know in advance what my second range will be at
design time; only at run-time, but for this particular instance I don't want
the overlapping values counted twice. How can I do this using ..?

Now, as long as we're just adding large numbers of random characters to make
our comparisons look better; i.e.,
foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))


I would say that

foreach (a in Seq(1,100) + Seq(200, 300))

looks far better than

foreach (a in ![{1............................................100,
200............................................300 }]!)

Don't you? LOFL

Thanks,
Michael C.


Nov 16 '05 #54

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:Ov**************@TK2MSFTNGP09.phx.gbl...

"Michael C" <mi*******@optonline.net> wrote in message
news:ON**************@TK2MSFTNGP11.phx.gbl...

"cody" <pl*************************@gmx.de> wrote in message
news:ef**************@tk2msftngp13.phx.gbl...

Ahh, I thought the point was to be able to create sequences that would
allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.
As cody said, the original goal was to make for's easier to write, in
esscense. Its not quite the same because you can't manually advance i, but
still, that was the goal.
The goal grew, for me anyway, to permit the language to succinctly express
lists and some list operations without using explict classes. Much of what

I want to see isn't expressly possible with existing syntax and having to
write a method to do it for each situation is overkill(one method is 10
minutes maybe, but if you have to do it a hundred times a month?). A good
deal of what I'm looking at and driving towards is python\haskell like lists and list comprehensions.

Generalizing the methodology is important.

I didn't realize the only acceptable option was a language re-design. I
suppose I don't see the benefit of changing the language syntax in order
to
accomplish something that can be done in half an hour.

Language syntax extension isn't the only acceptable solution(although it

is for the original concept). However I don't think there is any harm in
exploring new syntax that would ease certain concepts in the language. After all, there are quite a few things you don't *have* to have in the language
that are quite useful, like using, foreach, lock, switch, break, continue,
goto, else, etc. They make the language easier even though you could use
other constructs to achieve the same effective result if not the exact code.
I agree that a lot of what is currently in the language is handy, though not
critical.
Part of the purpose of these implementations is to suggest ideas and examine them by using them. This allows you to really get an idea of how the code
would look and work in real scenarios. I've written a number of
modifications to the language, some were throwaway, some were 'just seeing
if I can', and others where things I would really like to see change in the language. Its a hobby and an excercise, and maybe one of my ideas may filter up and actually affect change. I don't think there is anything really wrong with that, as long as the idea is a good one and is generally considered a
good one.

Nope, there's nothing wrong with working through theoretical solutions. I
do it all the time myself.
Considering I am going to implement this, I obviously support it. However, I would and do appreciate any comments you have on the syntax as it stands(as explained in other parts of this thread) as well as on its actual nessecity, even if they are negative. For what its worth, I do happen to agree with you that the syntax isn't absolutly vital, as everything I have planned can be
packaged up into a class(with anonymous methods list comprehensions), I just think that readability, ease of authoring, and the optimization potential
warrents atleast implementing and using the syntax for a while. Hopefully an implementation wil atleast spark a little more discussion about its merits
and bring in some ground support and clearly inform us that the community as a whole doesn't like it(as opposed to simply not bothering getting into the discussion).


As you could probably tell from my previous posts, I'm all about saving wear
and tear on the fingertips. But I also like solutions that are general
enough to be useful in a variety of situations. The operators you've come
up with, while they seem like they'll save some time and effort in very
specific circumstances, still seem to fall back to creating general classes
that can be optimized by the compiler in very specific circumstances (i.e.,
in looping constructs in which the range defined by 2 terminal constants).
They also seem to be very limited use -- how do you propose the creation of,
and performing operations on, dynamic ranges at run-time?

I appreciate your response.

Cheers,
Michael C.
Nov 16 '05 #55
> > > Ahh, I thought the point was to be able to create sequences that would
allow
you to iterate over all members of the sequence/range, or multiple
sequences/ranges, using foreach.
Indeed, that was the point.
I didn't realize the only acceptable option was a language re-design. I suppose I don't see the benefit of changing the language syntax in
order to
accomplish something that can be done in half an hour.


You'll agree, that foreach (a in 1..100, 200..300) is far better than

foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))

don't you? And exactly that is the purpose of my proposal.
Loops are one of the most common used things and making them more readable and maintainable should be a desirable goal, don't you agree?


'Better' is a subjective term; and I actually don't agree that it's
'better'. BTW, I'm sure you haven't noticed, but I've been with this

thread from the beginning. In fact, I was the first poster to defend your general ideas about improving the quality of life for programmers after your ideas
were initially blasted by other posters.

Maybe you've read the thread but you seem not to understand the problem.
I have a sequence 1..100. Is it possible to create a dynamically generated sequence at run-time using your method like this:

int x = 1;
int y = 100;
foreach (a in x..y)

Sure. Why should that be a problem.
Now, assuming I want to specifically exclude 10..20 or 30..40 based on a
user response at run-time, how do I accomplish this using ..?

Nobody was talking here about excluding things from a range. It it not
possible
and I do not see very much benefit in it. You can declare a fixed number of
ranges
that might overlap and the arguments are evaluated at runtime.
Additionally we were talking about stating increment values for cases like
datetime
which cannot be incremented simply by ++. Extra syntax would be required and
I don't think it is nessecary to introducing something similar to the very
funny
syntax you just suggested.
foreach (a in SequenceBuilder.GetSequence(1, 100) +
SequenceBuilder.GetSequence(200, 300))


I would say that

foreach (a in Seq(1,100) + Seq(200, 300))

What is Seq? If it is a static method it has to be declared in the current
class to use it like that, otherwise you have to state the class name in
front of the method. If the method is nonstatic you have to provide and
object, you know.
One possibility would be to use a ctor which would then look like this
(provided that the class Seq's namespace is imported):

foreach (a in new Seq(1,100) + new Seq(200, 300))

And again, nobody would create a new class and use it everywhere in his
project only to make its loops look better and perform much slower. Would
YOU do that? Surely not.

But if you simply could write foreach (int a in 100..0) wouldn't you used
that instead of for (int i=100; i>=0; i--)? Sure you would.

compare this:

// 3 accesses to the variable in the loop head are redundant and lead to
mistakes:
// It often happens to me that I mix i up with j and similar or
inadvertently use ++ instead of --.
for (int i=w; i>=0; i--)
for (int j=h; j>=0; j--)
{
Foo(i * j);
}

with that:

foreach (i in w..0)
foreach (j in h..0)
{
Foo(i * j);
}

Which one is more readable?

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #56
> I agree that a lot of what is currently in the language is handy, though
not
critical.


Why "for", "while" and "switch" if we have goto! Why "string" if we have
char[]?
Why virtual methods if we have delegates to fake them? Why even classes?
Why foreach (a in 0..100) if we already have goto???

It is at the end all just syntactic sugar.

:)

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #57
> >> 4) For enumerableExpression, every value is read out *unless* the
expression
is cast to object or somesuch. This does leave a problem in that there is
no
way to generate a list of IEnumerables, but if someone wants that edge case
they should probably use long-hand list or array syntax. Can you think
of any workarounds for this?
enumerableExpression is typed either object for IEnumerable or T for
IEnumerable<T>.


I do not like that Idea. It would mean that you cannot put any object
supporting IEnumerable into the array without splitting it up into its
items. Better would be IEnumerator instead of IEnumerable, which means
you'll have to put list.GetEnumerator() into the array so it'll be
splitted
up. But I still do not like that idea. Array have a fixed size and
allowing
IEnumerators to be inserted into array literals means that during

runtime you have to create temporary lists and copy from them into the destination array since you cannot resize it.
e.g:

{obj1, obj2, list1}

Assumed you want to guarantee that the expressions are evaluated in the
order they appear in the declaration you have first to create a temporary list. you put obj1 and obj2 in it. then you have to evaluate list1 and add its contents to the list. after you've evaluated all objects you can call .ToArray() and your array is constructed.
But again, I do not like the idea of expanding IEnumerables. If I declare an
array literal containg 3 elements I expect that is really has 3 elements
and
not more.

The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without

breaking the rules of the language is pretty tough. I've been considering a new
operator, <-, which assigns each value in an enumerable object to a variable
Now I understand. Maybe you can treat x..y as a special type, because in
contrast to an IEnumerable the size of the range is known at execution time
and has not to be enumerated first, so no temporary arraylist has to be
generated as it would otherwise be the case.

But I'd rather make the assigment of all subitems to the array explicit than
implicit,
so I vote for your "content operator", although I'd rather use # instead of
<-.
I'm talking optimization of range literals. For the case foreach (int i in
1...1000) I see no reason to generate an object, instead a for loop like you suggested is generatable. However, for code like
foreach(int i in 1...1000, 1000...1, 2...5 : .5, 3...5838) a generator
object would probably make more sense as it would *vastly* simplify the
output code and probably the break statement as well.

But for the compiler it won't make any difference wheather you have 2 or 20
ranges in the loop will it?
Outputting for each range a separate loop is the most performant solution.
The only problem is if the loop *body* is very large you really have a code
bloat.
In that case outputting a single foreach loop which iterates over a
constructed RangeEnumerator object will be the right way.
Since this is a proof-of-concept implementation, I am also looking at
slightly more complicated subjects like haskell&python like list
comprehensions(which I think effectivly become mini-iterators). My existing implementatino ideas are too unpolished to post right now(6 pages of
rambling is to much), but the current syntax allows you to do things like
sequence unions and intersections, value mutation, value analysis, etc.
some rough samples:
[1...1000 where value%2 == 0] //returns all even numbers between 1 &1000,
inclusive
[yield Math.Pow(value,2) for 1...1000 where Math.Pow(value,2)%2 == 0]
//returns the square of each value which has an even square

Now you are really going to far :) Are you sure this would fit into C#?
Your first example can be solved using the optionally increment clause
and for more complicated things I would create my own enumerator.

foreach (int a in GetAllPowerOfTwoWherePowerOfTwoIsEven(1, 1000))
{
list.Add(a);
}

Using C#'s new anonymous methods you could do:

[delegate{foreach(int a in 1..1000){if (Math.Pow(value,2)%2 == 0)yield
return Math.Pow(value,2); }}]

Btw, a great enhancement for the ArrayList class would be an AddRange method
and a Ctor that takes an IEnumerable as parameter which fills the list with
all enumerated elements.
But, as I said these are *very* rough and I'm not sure how or if I'll
bother. I do like the concept though and they are quite popular in python.

I'll post more on syntax reasoning if anyone is interested.

Iam very interested in it. Do you have a homepage?

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #58
Your solution is very limited in scope and functionality. It assumes that
everyone who decides to use ranges wants to be bound to a couple of strict
rules (overlapping members, static range declarations, ...), offering no
alternatives. All this is well and good, as the ranges are generated
non-dynamically at compile-time. This would only be a real issue if your
solution was flexible enough to produce ranges dynamically at run-time where
the following conditions apply:

-The programmer might not have absolute control over which items are
included in the range to be looped over
-The programmer wants a specific behavior every time regardless of the items
to be included

As you already know, speed and flexibility are almost inverse to one
another. Since we're putting out our wish list here, to me the best
solution would be one in which all the functionality of a class could be
combined with high-speed optimization technologies. I.e., all ranges that
could be generated statically at compile-time would be, and full-featured
dynamic ranges would be relegated to being created at run-time.

That's all I have to say about that.

Michael C.
Nov 16 '05 #59

"cody" <pl*************************@gmx.de> wrote in message
news:%2***************@tk2msftngp13.phx.gbl...
I agree that a lot of what is currently in the language is handy, though

not
critical.


Why "for", "while" and "switch" if we have goto! Why "string" if we have
char[]?
Why virtual methods if we have delegates to fake them? Why even classes?
Why foreach (a in 0..100) if we already have goto???

It is at the end all just syntactic sugar.

:)


(See my post where I mention the usefulness of the "?:" operator). I agree
totally, and if static range declarations that are locked in place at
compile time is the answer to your problems, more power to ya. But
considering the static nature of these range declarations, what is the
usefulness of combined range declaration like 0..100, 50..150 in a foreach
loop? It seems like the MER (Maximum Effective Range) of the following
statement would be nil:

foreach (a in 0..100, 50..150)

And the MER of this one would be maybe a little bit better:

foreach (a in 0..100, 200..300)

Thanks,
Michael C.
Nov 16 '05 #60
>> The problem here is that 1...3 *would* basically be an
IEnumerable\IEnumerator. It is a sequence generator which is expressed by
enumerable objects in the runtime. Making all of this work without breaking
the rules of the language is pretty tough. I've been considering a new
operator, <-, which assigns each value in an enumerable object to a

variable
Now I understand. Maybe you can treat x..y as a special type, because in
contrast to an IEnumerable the size of the range is known at execution
time
and has not to be enumerated first, so no temporary arraylist has to be
generated as it would otherwise be the case.

Treating it as a special type is a possibility, but I am in general hesitant
to define behaviour based on syntax instead of semantics. But I'd rather make the assigment of all subitems to the array explicit
than
implicit,
so I vote for your "content operator", although I'd rather use # instead
of
<-.
I'm still considering this particular issue. Finding good syntax is pretty
tough. The original concept behind <- was based around another piece of
syntax which I've more or less discounted at this point.
A different, perhaps better perhaps not, solution may be to perform
automated input on expressions typed IEnumerable explicitly instead of any
other, yet I'm still not sure.
I am concerned with the # syntax due to # being used already by compiler
directives.
I'm talking optimization of range literals. For the case foreach (int i
in
1...1000) I see no reason to generate an object, instead a for loop like you
suggested is generatable. However, for code like
foreach(int i in 1...1000, 1000...1, 2...5 : .5, 3...5838) a generator
object would probably make more sense as it would *vastly* simplify the
output code and probably the break statement as well.

But for the compiler it won't make any difference wheather you have 2 or
20
ranges in the loop will it?
Outputting for each range a separate loop is the most performant solution.
The only problem is if the loop *body* is very large you really have a
code
bloat.
In that case outputting a single foreach loop which iterates over a
constructed RangeEnumerator object will be the right way.


It won't make significant difference to the compiler, but as you noted if
the code starts to bloat or conditions start to grow to much the compiler
should shift. I imagine there is a point where the enumerator is more
performant than inline for's and you would certainly not want a huge amount
of code generated due to a single expression. For starters I'll probably
only support optimizing 2 ranges and consider adding additional
optimizations if I think it makes sense. It is only a proof of concept after
all.
Since this is a proof-of-concept implementation, I am also looking at
slightly more complicated subjects like haskell&python like list
comprehensions(which I think effectivly become mini-iterators). My

existing
implementatino ideas are too unpolished to post right now(6 pages of
rambling is to much), but the current syntax allows you to do things like
sequence unions and intersections, value mutation, value analysis, etc.
some rough samples:
[1...1000 where value%2 == 0] //returns all even numbers between 1 &1000,
inclusive
[yield Math.Pow(value,2) for 1...1000 where Math.Pow(value,2)%2 == 0]
//returns the square of each value which has an even square

Now you are really going to far :) Are you sure this would fit into C#?
Your first example can be solved using the optionally increment clause
and for more complicated things I would create my own enumerator.


Nope, I'm not sure about anything, ;). The entire point of this is to
experiment with ti and see how it works. List comprehensions are fairly
popular in the functional world and I am rather curious as to if they *can*
be fit into C# and if they will make life easier or not. I figure if I'm
going this far I may as well do the whole shebang and see what everyoen
thinks after they use it.

foreach (int a in GetAllPowerOfTwoWherePowerOfTwoIsEven(1, 1000))
{
list.Add(a);
}
yes and you can say foreach(int a in GetAllNumbersBetween(1,1000))
{
list.Add(a);
}

just as well. It's kind of illogical to use the argument you are fighting
for your base concept to argue down another feature, isn't it? :) Using C#'s new anonymous methods you could do:

[delegate{foreach(int a in 1..1000){if (Math.Pow(value,2)%2 == 0)yield
return Math.Pow(value,2); }}]
I don't think you can use iterators in anonymous methods, though I could be
wrong.

Btw, a great enhancement for the ArrayList class would be an AddRange
method
and a Ctor that takes an IEnumerable as parameter which fills the list
with
all enumerated elements.


Yeah, that would make sense.
But, as I said these are *very* rough and I'm not sure how or if I'll
bother. I do like the concept though and they are quite popular in
python.

I'll post more on syntax reasoning if anyone is interested.

Iam very interested in it. Do you have a homepage?


No, sadly. Me and http are not particulary close friends(this is probably
due to my long running campaign to discredit http, html, and most related
technologies as garbage). As such I don't really keep web pages going too
often. I did have a blog[1] for a little while but after a few posts I
stopped using it.

Most of my ideas show up here sometime or another. I will try to writeup all
the various things I've considered *or* I will forward port all my mono
modifications and provide a complete patch containing these list features
with everything else.

1. http://www.dotnetjunkies.com/weblog/doconnell/

Maybe I'll start updating things there as work gets done on this feature.
Nov 16 '05 #61

As you could probably tell from my previous posts, I'm all about saving
wear
and tear on the fingertips. But I also like solutions that are general
enough to be useful in a variety of situations. The operators you've come
up with, while they seem like they'll save some time and effort in very
specific circumstances, still seem to fall back to creating general
classes
that can be optimized by the compiler in very specific circumstances
(i.e.,
in looping constructs in which the range defined by 2 terminal constants).
They also seem to be very limited use -- how do you propose the creation
of,
and performing operations on, dynamic ranges at run-time?


I am considering several things, like list comprehensions, which extend the
syntax sufficently that its valid outside of foreach and isin clauses. I'm
not sure I can do it, but I'm confident I can make a good run at it.

For starters, for dynamic ranges I hope to be able to optimize fairly well.
The syntax I am considering for basic dynamic ranges will be something like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

Is that what you mean?

As far as I'm concerned code like:
GetValue()...GetOtherValue();
should form a valid range expression. Its possible the range would be
strange(25...-2) for example, where the numbers will operate in the way
numbers do on computers strictly due to the way computers work. It has a
chance for a bug, but many features do.

Also, IMHO, 1... means every number greater than one(this would stop as soon
as a number was less than one, in other words when wrapover occurs). and ...
would be effectivly an infinite sequence, or basically every number possible
for the assocaited type, starting from the minimum value of the type and
going to the max. Creating actual behaviour to that extent is pretty
impossible I think, but thats logically waht it should mean.

Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

The range syntax is valuable mainly beacuse it creates a *standard* way of
expressing a large range of numbers compactly. Everyone can write their own
range class, but everyone's range class is probably going to have slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital to
the overreaching features I'm looking at. As I said early on, the original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins using
a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates a
list containing all three character long strings.

Now, I only hope these things help create a syntax that has a compelling
purpose, they may not. I certainly won't declare they are good ideas,
however its worth a shot isn't it?

Also, none of htese features are new. Its not a matter of "does it work",
but a matter of "does it work with C#"

Nov 16 '05 #62

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:%2****************@TK2MSFTNGP09.phx.gbl...

As you could probably tell from my previous posts, I'm all about saving
wear
and tear on the fingertips. But I also like solutions that are general
enough to be useful in a variety of situations....
I am considering several things, like list comprehensions, which extend

the syntax sufficently that its valid outside of foreach and isin clauses. I'm
not sure I can do it, but I'm confident I can make a good run at it.

For starters, for dynamic ranges I hope to be able to optimize fairly well. The syntax I am considering for basic dynamic ranges will be something like: int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

Is that what you mean?

No. This is still has a static declaration at run-time. The best example
of this goes back to joining multiple ranges together. For instance 1..100,
200..300 as in

int x = 1;
int y = 100;
foreach (a in x..y, 200..300)

Obviously you can change the values of x and y at run-time which gives a
little flexibility. If you have a need to dynamically add or remove ranges
to/from this foreach statement at run-time, how does the proposal address
this, other than creating another foreach loop? Here's an example:

bool userflag;
bool userflag2;
foreach (a in 1..100, 200..300) { /*code here*/ }
//now based on a flag set by a user selection I want to add another range
if (userflag)
foreach (a in 400..500) { /* programmer cuts and pastes same code from
loop above? */ }
//now based on a second flag we'll add another range
if (userflag2)
foreach (a in 600..700) { /* programmer cuts and pastes same code from
loop above? */ }

Now let's say we want to exclude the range 50..75 based on another user
flag, using the same code as above we replace the first foreach statement
with:

bool userflag3;
if (userflag3)
foreach (a in 1..49, 76..100, 200..300) { /* code here */ }
else
foreach (a in 1..100, 200..300) {/* cut and paste code from above */}

This is why I consider the solution to be non-dynamic, as each foreach loop
is broken down into un-changeable for statements at compile-time. Now if we
had 50 lines in each of the code blocks, we've suddenly expanded our program
to well over 250+ lines before we even bother expanding the foreach loops
into for loops, correct? As I said before, there is a trade-off, and the
performance will come at some cost. In this case the static nature of the
foreach (..) implementation gives us greater speed, but less flexibility in
defining and performing operations on ranges, and it expands the source code
to over 5X it's original size; which is not making the programmer's life
much easier IMHO.
As far as I'm concerned code like:
GetValue()...GetOtherValue();
should form a valid range expression. Its possible the range would be
strange(25...-2) for example, where the numbers will operate in the way
numbers do on computers strictly due to the way computers work. It has a
chance for a bug, but many features do.

I think code like that should form valid range expressions also, IMHO.
Also, IMHO, 1... means every number greater than one(this would stop as soon as a number was less than one, in other words when wrapover occurs). and .... would be effectivly an infinite sequence, or basically every number possible for the assocaited type, starting from the minimum value of the type and
going to the max. Creating actual behaviour to that extent is pretty
impossible I think, but thats logically waht it should mean.

That makes sense. Creating a function (assuming there isn't one I've
overlooked) that returns the maximum and minimum values for any given
numeric type wouldn't be too difficult, one would think. Implementing the
.... operator would be the difficult part, I assume.
Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

I don't disagree with that. So are you simply defining a new syntax to make
declaring Lists easier? If so, why aren't you just iterating over a List in
the foreach statements?
The range syntax is valuable mainly beacuse it creates a *standard* way of
expressing a large range of numbers compactly. Everyone can write their own range class, but everyone's range class is probably going to have slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital to the overreaching features I'm looking at. As I said early on, the original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins using a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates a list containing all three character long strings.

99% of the time the programmer is not going to have a static list like
["mouse", "cat", "dog"] to put in their list as in the example above.
Obviously if I wanted to save myself typing 80% of what you did above, I
would simply type:

["cat", "dog"]

which is equivalent, no? The usefulness of this would come in when I can
change my list at run-time and perform that yield operation on a List
variable.
Now, I only hope these things help create a syntax that has a compelling
purpose, they may not. I certainly won't declare they are good ideas,
however its worth a shot isn't it?

Everything is worth a shot if there's someone willing to do it.
Also, none of htese features are new. Its not a matter of "does it work",
but a matter of "does it work with C#"


That makes sense, but again, the MER and the value proposition of the ranges
is small due to their limited scope and ability. As part of a simplified
List declaration syntax I can see your ranges adding value by reducing the
programmer's work significantly. But if this is what they reduce to, then
why use them outside of a List declaration scope at all? And why use them
independently of Lists at all? I.e., you don't foreach over a range, you
foreach over a List containing that range.

Michael C.
Nov 16 '05 #63
> Your solution is very limited in scope and functionality. It assumes that
everyone who decides to use ranges wants to be bound to a couple of strict
rules (overlapping members, static range declarations, ...), offering no
alternatives. All this is well and good, as the ranges are generated
non-dynamically at compile-time. This would only be a real issue if your
solution was flexible enough to produce ranges dynamically at run-time where the following conditions apply:

-The programmer might not have absolute control over which items are
included in the range to be looped over
-The programmer wants a specific behavior every time regardless of the items to be included


Be honest: In 99% of all cases you use simple loops like for (int i=0;
i<100; i++).
Often they loop reverse or they are nested. And our proposal is just for
these cases.

I can hardly imagine a scenario where you really want to enumerate over
multiple ranges and the ranges are know during runtime only and additionally
you want to exclude numbers from your enumeration.
This is somewhat rarely for such a case I'd rather use an Enumerator which
can be made so flexible that is covers all cases.

Don't you agree that it would be a bit stupid to invent a language feature
for a case which is so rarely used?

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #64
(See my post where I mention the usefulness of the "?:" operator). I agree totally, and if static range declarations that are locked in place at
compile time is the answer to your problems, more power to ya. But
considering the static nature of these range declarations, what is the
usefulness of combined range declaration like 0..100, 50..150 in a foreach
loop? It seems like the MER (Maximum Effective Range) of the following
statement would be nil:

foreach (a in 0..100, 50..150)

This loop does 202 iterations. I hope you've read the thread.

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #65
> I am concerned with the # syntax due to # being used already by compiler
directives.


Damn you're right. I fear that there is no single-char operator left to use.
Maybe <- will be the right choice :)

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #66
> For starters, for dynamic ranges I hope to be able to optimize fairly
well.
The syntax I am considering for basic dynamic ranges will be something like: int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

I found a problem. What if x is greater than y? In that case the loop has to
loop backward and this
decision can only be determined at runtime, so the generated for loop will
look a bit more complicated:

float inc;
float sgn;
if (x>y)
{
inc = - 1f;
sgn = - 1f;
y= - (y+1);
}
else
{
inc = 1f;
sgn = 1f;
y++;
}

for (int i = x; sgn * i <= y; i+=inc)
{
//code
}

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #67

"cody" <pl*************************@gmx.de> wrote in message
news:OE**************@tk2msftngp13.phx.gbl...
For starters, for dynamic ranges I hope to be able to optimize fairly well.
The syntax I am considering for basic dynamic ranges will be something

like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

I found a problem. What if x is greater than y? In that case the loop has

to loop backward and this
decision can only be determined at runtime, so the generated for loop will
look a bit more complicated:


That seems like a big assumption! Often x being greater than y indicates
the loop should not be performed at all.
Nov 16 '05 #68

"Daniel Billingsley" <dbillingsley@NO_durcon_SPAAMM.com> wrote in message
news:%2****************@tk2msftngp13.phx.gbl...

"cody" <pl*************************@gmx.de> wrote in message
news:OE**************@tk2msftngp13.phx.gbl...
> For starters, for dynamic ranges I hope to be able to optimize fairly well.
> The syntax I am considering for basic dynamic ranges will be something

like:
> int x = 50;
> int y = 1058;
> foreach (int i in x...y)
> {
> //code
> }
>
> which should translate to
> for (int i = x; i <= y; i++)
> {
> //code
> }

I found a problem. What if x is greater than y? In that case the loop has

to
loop backward and this
decision can only be determined at runtime, so the generated for loop
will
look a bit more complicated:


That seems like a big assumption! Often x being greater than y indicates
the loop should not be performed at all.

Or even that you should use wrap-around semantics. Determining that
behaviour could be rather difficult but not impossible.

This is a semantic area that needs definition, however. Do ranges always
head fowards or do they take go towards y?

Nov 16 '05 #69

"cody" <pl*************************@gmx.de> wrote in message
news:%2****************@TK2MSFTNGP11.phx.gbl...
(See my post where I mention the usefulness of the "?:" operator). I

agree
But
considering the static nature of these range declarations, what is the
usefulness of combined range declaration like 0..100, 50..150 in a foreach loop? It seems like the MER (Maximum Effective Range) of the following
statement would be nil:

foreach (a in 0..100, 50..150)

This loop does 202 iterations. I hope you've read the thread.


So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness
of the foreach example above?

Michael C.
Nov 16 '05 #70

"cody" <pl*************************@gmx.de> wrote in message
news:uz**************@TK2MSFTNGP09.phx.gbl...
Be honest: In 99% of all cases you use simple loops like for (int i=0;
i<100; i++).
Often they loop reverse or they are nested. And our proposal is just for
these cases.

Be honest. Based on yours and Daniel's posts you have proposed lot more
than a short-hand 'for' loop. If it's a simplified syntax for a for loop
you're looking for, then it's not nearly as complex as you make it. You've
been suggesting multiple linked ranges, ranges seen as subsets of Lists (yet
not part of a class), etc. If you're covering only the 99% of for loops out
there, then be honest - why do you need all this other stuff? This
statement:

foreach (i in 1..100, 50..150)

Falls WAY outside your 99% rule. I'd be surprised to find anyone, outside
of some extreme statistical analysis maybe, who would need a loop like this
for anything. I could be wrong though - maybe you can enlighten me on the
reasoning and usage of a looping structure like this?
I can hardly imagine a scenario where you really want to enumerate over
multiple ranges and the ranges are know during runtime only and additionally you want to exclude numbers from your enumeration.
This is somewhat rarely for such a case I'd rather use an Enumerator which
can be made so flexible that is covers all cases.

I can imagine many more times where you really want to enumerate over
multiple ranges and the ranges are known only at run-time than scenarios in
which you want to enumerate over multiple ranges and the ranges are known in
advance. I've given you examples; the prime one being a range which is
iterated over in which the user specifically chooses to exclude certain
sub-ranges or elements. I'd love to hear some examples of enumerating over
multiple ranges and knowing all the ranges at design-time.
Don't you agree that it would be a bit stupid to invent a language feature
for a case which is so rarely used?


Exactly! Now you're getting it!
Nov 16 '05 #71

"Michael C" <mi*******@optonline.net> wrote in message
news:%2****************@TK2MSFTNGP10.phx.gbl...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:%2****************@TK2MSFTNGP09.phx.gbl...
>
> As you could probably tell from my previous posts, I'm all about saving
> wear
> and tear on the fingertips. But I also like solutions that are general
> enough to be useful in a variety of situations....
I am considering several things, like list comprehensions, which extend

the
syntax sufficently that its valid outside of foreach and isin clauses.
I'm
not sure I can do it, but I'm confident I can make a good run at it.

For starters, for dynamic ranges I hope to be able to optimize fairly

well.
The syntax I am considering for basic dynamic ranges will be something

like:
int x = 50;
int y = 1058;
foreach (int i in x...y)
{
//code
}

which should translate to
for (int i = x; i <= y; i++)
{
//code
}

Is that what you mean?


No. This is still has a static declaration at run-time. The best example
of this goes back to joining multiple ranges together. For instance
1..100,
200..300 as in

int x = 1;
int y = 100;
foreach (a in x..y, 200..300)

Obviously you can change the values of x and y at run-time which gives a
little flexibility. If you have a need to dynamically add or remove
ranges
to/from this foreach statement at run-time, how does the proposal address
this, other than creating another foreach loop? Here's an example:

bool userflag;
bool userflag2;
foreach (a in 1..100, 200..300) { /*code here*/ }
//now based on a flag set by a user selection I want to add another range
if (userflag)
foreach (a in 400..500) { /* programmer cuts and pastes same code from
loop above? */ }
//now based on a second flag we'll add another range
if (userflag2)
foreach (a in 600..700) { /* programmer cuts and pastes same code from
loop above? */ }

Now let's say we want to exclude the range 50..75 based on another user
flag, using the same code as above we replace the first foreach statement
with:

bool userflag3;
if (userflag3)
foreach (a in 1..49, 76..100, 200..300) { /* code here */ }
else
foreach (a in 1..100, 200..300) {/* cut and paste code from above */}


Well, like many things, you use a slightly different pattern here.

IEnumerable<int> enum= 1...100,1...200;
IList<int> list;
if (userflag)
enum = enum,400...500;
if (userflag2)
enum = enum,500...600;
if (userflag3)
list = [yield value for enum where !(value isin [50...75])];
else
list = [enum];

foreach (int i in list)
{
//code
}
(note the syntax isn't finalized yet. This will result in different
enumerators for each value. However I'm not terribly thrilled with the
multiple enumerator definition syntax)

is probably what I would do. This is precisely where list comprehensions
come in, they allow you to modify what ranges and other lists generate for
your list. This is a circumstance that I consider to be too complex to
achieve for optimization, this would result in a foreach loop going over a
list containing the values(although I would probably design the list so that
it doesn't generate values until used, taking up less space in the case of
large numbers).
This is why I consider the solution to be non-dynamic, as each foreach
loop
is broken down into un-changeable for statements at compile-time. Now if
we
had 50 lines in each of the code blocks, we've suddenly expanded our
program
to well over 250+ lines before we even bother expanding the foreach loops
into for loops, correct? As I said before, there is a trade-off, and the
performance will come at some cost. In this case the static nature of the
foreach (..) implementation gives us greater speed, but less flexibility
in
defining and performing operations on ranges, and it expands the source
code
to over 5X it's original size; which is not making the programmer's life
much easier IMHO.

Breaking down to for loops only happens when the pattern used allows it.
Using literal patterns in a foreach as such is identical to using them in a
for. There is no way to dynamically change it anymore than its possible to
change an upper bound in a for. However, in both situations you can use
other patterns, as I showed above, to achieve what you want with a minimum
of work.
As far as I'm concerned code like:
GetValue()...GetOtherValue();
should form a valid range expression. Its possible the range would be
strange(25...-2) for example, where the numbers will operate in the way
numbers do on computers strictly due to the way computers work. It has a
chance for a bug, but many features do.


I think code like that should form valid range expressions also, IMHO.


As cody pointed out, there is a difficulty in getting values to behave
correctly here, however. I am concerned that dynamic sources could be
confusing. But that will be addressed when I come to it.
Also, IMHO, 1... means every number greater than one(this would stop as soon
as a number was less than one, in other words when wrapover occurs). and

...
would be effectivly an infinite sequence, or basically every number

possible
for the assocaited type, starting from the minimum value of the type and
going to the max. Creating actual behaviour to that extent is pretty
impossible I think, but thats logically waht it should mean.


That makes sense. Creating a function (assuming there isn't one I've
overlooked) that returns the maximum and minimum values for any given
numeric type wouldn't be too difficult, one would think. Implementing the
... operator would be the difficult part, I assume.


It won't be difficult for the built in types, its more custom valuetypes I'm
concerned with. Since the design supports arbitrary increments its possible
some more complex logic needs to be defined to deal with that cirucmstance.

Basically, IMHO, max is the value where adding one to it would make it less
than itself.
Of course, the root features allow a few different things. First and
foremost the [] syntax allows for lists. It is *far* easier to write [
"cat", "dot", "mouse"] than it is to write
List<string> list = new List<string>();
list.Add("cat");
list.Add("dog");
list.Add("mouse");

I don't disagree with that. So are you simply defining a new syntax to
make
declaring Lists easier? If so, why aren't you just iterating over a List
in
the foreach statements?


Defining lists is a core purpose, as is ranges.
The range syntax is valuable mainly beacuse it creates a *standard* way
of
expressing a large range of numbers compactly. Everyone can write their own
range class, but everyone's range class is probably going to have
slightly
different semantics, making things a bit toughter. Framework or compiler
support helps significantly. Also as cody has pointed out, 1...1000 is
clearer, mathematically and logically, than GetRangeOf(1, 1000);
Personally I consider the ranges as a minor feature that is pretty vital

to
the overreaching features I'm looking at. As I said early on, the
original
proposal didn't reach far enough. Ranges are a supporting feature of list
literals and list comprehensions.

List comprehensions may be useful. Based on the syntax I'm using now, you
can generate an enumerator from a list based on simple qualificatoins

using
a single line. Basically list comprehensions are one-line, single shot
iterators:

[yield value for ["mouse","cat","dog"] where value.Length == 3] generates

a
list containing all three character long strings.


99% of the time the programmer is not going to have a static list like
["mouse", "cat", "dog"] to put in their list as in the example above.
Obviously if I wanted to save myself typing 80% of what you did above, I
would simply type:

["cat", "dog"]

which is equivalent, no? The usefulness of this would come in when I can
change my list at run-time and perform that yield operation on a List
variable.


Yes it is. However you can use *any* IEnumerable object in your list
comperehension and that object will be iterated and yielded into the new
list. I was trying to figure out a way to handle multiple source lists in a
comprehension cleanly, but I have failed so far. To do it cleanly requires
implicit typing and other, less appealing, syntactic changes.
Now, I only hope these things help create a syntax that has a compelling
purpose, they may not. I certainly won't declare they are good ideas,
however its worth a shot isn't it?

Everything is worth a shot if there's someone willing to do it.
Also, none of htese features are new. Its not a matter of "does it work",
but a matter of "does it work with C#"


That makes sense, but again, the MER and the value proposition of the
ranges
is small due to their limited scope and ability. As part of a simplified
List declaration syntax I can see your ranges adding value by reducing the
programmer's work significantly. But if this is what they reduce to, then
why use them outside of a List declaration scope at all? And why use them
independently of Lists at all? I.e., you don't foreach over a range, you
foreach over a List containing that range.


Well, optimization oppurtunity is one reason. The other and more pressing
reason is that I dislike creating an expression that is only valid within
another expression even if the expression could be used elsewhere. As I
pointed out above to create your dynamic enumerable, you can use those
directly without generating a list. If there is any reaon use for them
outside of list's, I can't say. However from a purists point of view I think
a user should be allowed to use them as such if he or she wishes.

As far as the compiler is concerned 1...100 is simply an IEnumerable<int>
expression and it operates upon it as it is. It may do extra checks to see
if the expression is a range that can be optimized, but beyond that it
doesn't treat it as a range, it treats it as an IEnumerable<int>.

This isnt to say someone could argue me down into putting ranges into lists
only, its just my current point of view.
Michael C.

Nov 16 '05 #72
> >> > int x = 50;
> int y = 1058;
> foreach (int i in x...y)
> {
> //code
> }
>
> which should translate to
> for (int i = x; i <= y; i++)
> {
> //code
> }
I found a problem. What if x is greater than y? In that case the loop
has to
loop backward and this
decision can only be determined at runtime, so the generated for loop
will
look a bit more complicated:


That seems like a big assumption! Often x being greater than y indicates the loop should not be performed at all.

Or even that you should use wrap-around semantics. Determining that
behaviour could be rather difficult but not impossible.

This is a semantic area that needs definition, however. Do ranges always
head fowards or do they take go towards y?

I would say that ranges should always loop from the first given value to the
second, that's why they are called ranges.

foreach (int a in 0..100) // forward
foreach (int a in 100..0) // backward

If somebody only want to loop only forward or only backward (that is, the
direction is know at compile time) he can use a normal loop.

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #73
> > > (See my post where I mention the usefulness of the "?:" operator). I
agree
But
considering the static nature of these range declarations, what is the
usefulness of combined range declaration like 0..100, 50..150 in a foreach loop? It seems like the MER (Maximum Effective Range) of the following statement would be nil:

foreach (a in 0..100, 50..150)

This loop does 202 iterations. I hope you've read the thread.


So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness
of the foreach example above?

My original proposal was a simple loop using a *single* range. multiple
ranges and other possible extensions are still discussed and nobody claimed
here that they *are* really useful, just that they *could* be useful.
They *can* be very useful in situations like if (myChar in
['a'..'z','A'..'Z']). Don't tell me about regex,
first regex would be slower in this situation and second this was just an
example.

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
Nov 16 '05 #74

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:O%****************@TK2MSFTNGP11.phx.gbl...

Thank you for your post. Your explanations help quite a bit. Of course
there's always the trade-off between optimization and functionality. It
would be nice to have the ability to perform operations like + to join two
lists together, or - to remove elements, or even * to join two lists without
overlap. This would make the previous examples a lot simpler, as you could
just use statements like:

myList = [1..1000];
if (userflag)
myList -= [400..500];
if (userflag2)
myList -= [600..700];
if (userflag3)
myList -= [50..75];
foreach (i in myList)

Thanks,
Michael C.
Nov 16 '05 #75

"cody" <pl*************************@gmx.de> wrote in message
news:e2**************@tk2msftngp13.phx.gbl...
> foreach (a in 0..100, 50..150)
This loop does 202 iterations. I hope you've read the thread.

So does for (i = 1; i <= 202; i++). So again I ask what is the usefulness of the foreach example above?

My original proposal was a simple loop using a *single* range. multiple
ranges and other possible extensions are still discussed and nobody

claimed here that they *are* really useful, just that they *could* be useful.
They *can* be very useful in situations like if (myChar in
['a'..'z','A'..'Z']). Don't tell me about regex,
first regex would be slower in this situation and second this was just an
example.


Now, going back to your original proposal. A simple loop with a single
range - is that your current proposal? You've used the overlapping ranges
in numerous examples yourself, and expressly proposed that the behavior is
different than a 'set', since duplicates would be allowed in overlapping
ranges. (myChar in ['a'..'z', 'A'..'Z']) is definitely a LOT more useful
than (myChar in ['a'..'z', 'h'..'w', 'A'..'Z', 'L'..'O']), but again you're
*not* overlapping in your example above. IMHO I don't see any place that
overlapping ranges, when used in your paradigm *could* be useful. Much less
*are* useful. I could see application for non-overlapping ranges; but if
your proposal is a simple short-hand single range for foreach loops, then
99% of this thread is moot.

Enjoy,
Michael C.
Nov 16 '05 #76

"cody" <pl*************************@gmx.de> wrote in message
news:OU**************@TK2MSFTNGP10.phx.gbl...

I would say that ranges should always loop from the first given value to the second, that's why they are called ranges.

foreach (int a in 0..100) // forward
foreach (int a in 100..0) // backward

If somebody only want to loop only forward or only backward (that is, the
direction is know at compile time) he can use a normal loop.


Actually he's talking about optimization in relation to the issue I brought
up earlier about creating *dynamic* ranges. I think you answered that one
with a 'no problem'. Well there is a slight problem:

int x = 100;
int y = 200;
foreach (a in x..y)

According to your theory, this should be expanded to

for (a = x; a <= y; a++)

Well, if the value of x becomes greater than y, and you want the behavior
described above, you can't very well optimize it at compile time, can you?

int x = 100;
int y = 200;
if (myFlag)
{
x = 200;
y = 100;
}
foreach (a in x..y)

Does this statement optimize with:

for (a = x; a <= y; a++)

Or is it:

for (a = x; a >= y; a--)

Guess we'll find out at run-time, right?

Thanks,
Michael C.
Nov 16 '05 #77

"Michael C" <mi*******@optonline.net> wrote in message
news:uP*************@TK2MSFTNGP12.phx.gbl...

"cody" <pl*************************@gmx.de> wrote in message
news:OU**************@TK2MSFTNGP10.phx.gbl...

I would say that ranges should always loop from the first given value to the

Actually he's talking about optimization in relation to the issue I

brought up earlier about creating *dynamic* ranges. I think you answered that one
with a 'no problem'. Well there is a slight problem:


I think it goes even beyone that if you're talking about dynamic ranges -
which I was. It may be the case in a particular situation that x is
sometimes going to be less than y and sometimes greater than y, and the
desired behavior is actually to not run the loop if it is greater, just like
in a normal for loop. So now according to cody's proposal in that case
you'd have to wrap this new style for loop in an if statement, and perhaps
more importantly, remember that you have to. yuck.
Nov 16 '05 #78

"Michael C" <mi*******@optonline.net> wrote in message
news:uP**************@TK2MSFTNGP11.phx.gbl...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:O%****************@TK2MSFTNGP11.phx.gbl...

Thank you for your post. Your explanations help quite a bit. Of course
there's always the trade-off between optimization and functionality. It
would be nice to have the ability to perform operations like + to join two
lists together, or - to remove elements, or even * to join two lists
without
overlap. This would make the previous examples a lot simpler, as you
could
just use statements like:

myList = [1..1000];
if (userflag)
myList -= [400..500];
if (userflag2)
myList -= [600..700];
if (userflag3)
myList -= [50..75];
foreach (i in myList)

I have two issues here. One is that I don't like adding psuedo-operators to
types which *may* contain real operators at some point. IList and List don't
provide any, but that doesn't mean other types that implement them won't.
I'm more comfortable using comprehensions to perform these operations,
although I've yet to figure out a way to write list unions and intersections
easily(My multi-source syntax worked, but I really disliked the implicit
typing that resulted).

The other issue, -[50...75] is confusing. Is it going to remove *every*
instance of 50...75 or just the first one, or just a segment running from 50
to 75? With a comprehension exactly what is going to happen is spelled out,
there is no room for confusion.

At this point I'm starting to really fret. I don't know how to achieve a
complete and cohesive solution. Every time I get a set of rules working I
find a fundamental type that violates those rules. I'm having a tough time
with strings now, a string is enumerable but will rarely be wanted to be
considered as such. I am probably going to have to define an override
operator(something that says "put this list in the list" instead of "put
this list's contents in the list", any recommendations on syntax?
Thanks,
Michael C.

Nov 16 '05 #79

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:Ok**************@TK2MSFTNGP09.phx.gbl...

I have two issues here. One is that I don't like adding psuedo-operators to types which *may* contain real operators at some point. IList and List don't provide any, but that doesn't mean other types that implement them won't.
I'm more comfortable using comprehensions to perform these operations,
although I've yet to figure out a way to write list unions and intersections easily(My multi-source syntax worked, but I really disliked the implicit
typing that resulted).

If other types implement these operators at some point, wouldn't it be
incumbent on the programmer to overload the operators if they wanted
different behavior?
The other issue, -[50...75] is confusing. Is it going to remove *every*
instance of 50...75 or just the first one, or just a segment running from 50 to 75? With a comprehension exactly what is going to happen is spelled out, there is no room for confusion.

Confusion is at the core of this project. Not a lot of the desired end
results have been agreed upon, nor the underlying problem defined. One
person defines the problem as simple x..y ranges, in which overlapping
segments make no sense; one sees the usefulness of multiple ranges with
endpoints that are non-contiguous, another thinks overlap may be useful
somewhere down the road to somebody; another person sees ranges as being
part of a larger list type which may contain overlapping ranges or
non-contiguous ranges. So let's take a step back.

If we are looking at [1..100] as a list, which may contain multiple ranges,
then let's start with the + operator. If the list allows multiple ranges,
the + operator would produce the following result:

[1..100] + [25..75] = [1..100, 25..75]

Therefore the += operator would produce similar results:

x = [1..100];
x += [25..75]; // x = [1..100, 25..75]

I suggested the * operator because a programmer might want to produce a
single range with no overlap in some situations (i.e., looping where overlap
makes no sense). The | operator might make sense here also:

[1..100] * [25..150] = [1..150]

This leads to the shorthand *= also. As for the minus operator, that is
dependent partially on the definition of the actual problem from above.
Regardless, my thoughts are that most programmers expect their minus
operator to work inversely to the plus operator. So the - operator, as
inverse of the + operator, would provide the following:

x = [1..100, 25..75];
x += [25..75]; // x = [1..100, 25..75, 25..75]
x -= [25..75]; // x = [1..100, 25..75] - removes one range, as + adds one
range

Perhaps an operator could be agreed upon that also removes all of a certain
type of ranges (i just put in the ! cause I'm running out of keyboard
chars -- think of it as a placeholder):

x = [1..100, 25..75];
x != [25..75]; // removes all subranges 25..75 from the list

These are just ideas, but the problem has to be clearly defined first.
At this point I'm starting to really fret. I don't know how to achieve a
complete and cohesive solution. Every time I get a set of rules working I
find a fundamental type that violates those rules. I'm having a tough time
with strings now, a string is enumerable but will rarely be wanted to be
considered as such. I am probably going to have to define an override
operator(something that says "put this list in the list" instead of "put
this list's contents in the list", any recommendations on syntax?


Don't fret, just take a step back and clearly define the problem in terms of
the end result. I have a pretty good idea of the end result I would like,
but it doesn't necessarily mesh with all the other ideas being thrown
around. With that in mind, how about + to put a list in the list, and | to
put the list's contents in the list w/ overlap and * to copy contents w/o
overlap... In that case:

[1..100] + [25..75] = [1..100, [25..75]]
[1..100] | [25..75] = [1..100, 25..75] // if this operator were implemented
with overlap
[1..100] * [25..150] = [1..150] // if this operator were implemented with no
overlap

How you would implement the list inside of a list, and what operator - if
any - to remove items from the list, is another story...

Thanks,
Michael C.
Nov 16 '05 #80

"Michael C" <mi******************************@getridofthis.com > wrote in
message news:cR*******************@news4.srv.hcvlny.cv.net ...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:Ok**************@TK2MSFTNGP09.phx.gbl...

I have two issues here. One is that I don't like adding psuedo-operators to
types which *may* contain real operators at some point. IList and List

don't
provide any, but that doesn't mean other types that implement them won't.
I'm more comfortable using comprehensions to perform these operations,
although I've yet to figure out a way to write list unions and

intersections
easily(My multi-source syntax worked, but I really disliked the implicit
typing that resulted).


If other types implement these operators at some point, wouldn't it be
incumbent on the programmer to overload the operators if they wanted
different behavior?


Well, consider this:
[1...100] is an IList<int>.
[1...100] + [1...22] is effectivly IList<int> + IList<int>. As such a +
operator that operates on ILists is needed. However, if you provide a class
that implements IList<T> and overload operator+, what happens? I'm more
concerned about the general meaning than anything else. A psuedooperator
that adds lists adds all lists, I don't think its particularly clean to
limit it to one kind of list. Thus, you have potential conflicts.
The other issue, -[50...75] is confusing. Is it going to remove *every*
instance of 50...75 or just the first one, or just a segment running from 50
to 75? With a comprehension exactly what is going to happen is spelled

out,
there is no room for confusion.


Confusion is at the core of this project. Not a lot of the desired end
results have been agreed upon, nor the underlying problem defined. One
person defines the problem as simple x..y ranges, in which overlapping
segments make no sense; one sees the usefulness of multiple ranges with
endpoints that are non-contiguous, another thinks overlap may be useful
somewhere down the road to somebody; another person sees ranges as being
part of a larger list type which may contain overlapping ranges or
non-contiguous ranges. So let's take a step back.


Confusion is often the nature of ideas discussed on public forums. There are
alot of ideas flying around, granted, and I'm tryign to cull through them
and get sane behaviour. I don't think that to many ideas or too many
opinions kills off a potential features value so much as makes it difficult
to discern the core idea and what needs to be done. Given enough time there
is a good chance a general consensus will be reached.

If not, no offense inteded to anyone, but as I am the one implementing it, I
am taking it upon myself to try to filter that and create the best possible
solution, even if one idea or another has to go by the wayside because of
it. The hard part is dumping my own ideas on the issue, however, ;).

Everyone's goals are a touch different. I'm pushing for a very abstract
concept where each individual syntax exists independently but can be
combined. I can't say for certain what exactly everyone else wants.

If we are looking at [1..100] as a list, which may contain multiple
ranges,
then let's start with the + operator. If the list allows multiple ranges,
the + operator would produce the following result:

[1..100] + [25..75] = [1..100, 25..75]

Therefore the += operator would produce similar results:

x = [1..100];
x += [25..75]; // x = [1..100, 25..75]

I suggested the * operator because a programmer might want to produce a
single range with no overlap in some situations (i.e., looping where
overlap
makes no sense). The | operator might make sense here also:

[1..100] * [25..150] = [1..150]

This leads to the shorthand *= also. As for the minus operator, that is
dependent partially on the definition of the actual problem from above.
Regardless, my thoughts are that most programmers expect their minus
operator to work inversely to the plus operator. So the - operator, as
inverse of the + operator, would provide the following:

x = [1..100, 25..75];
x += [25..75]; // x = [1..100, 25..75, 25..75]
x -= [25..75]; // x = [1..100, 25..75] - removes one range, as + adds one
range

Perhaps an operator could be agreed upon that also removes all of a
certain
type of ranges (i just put in the ! cause I'm running out of keyboard
chars -- think of it as a placeholder):

x = [1..100, 25..75];
x != [25..75]; // removes all subranges 25..75 from the list

These are just ideas, but the problem has to be clearly defined first.
I agree that there is a need to be able to express these ideas. I see you
prefer operators while I prefer the comprehension style. I am concerned
about operator overloading issues as above, but my primary goal with
comprehensions is that they are more flexible(and probably less performant,
bringing up your favorite adage again). A comprehension isn't limited to
joining, removing, and intersecting, but can be used to perform complex
operations on list contents, including transformations and lookups:
[yield GetValue(value) for 1...1000] for example;

I think the biggest problem is there *isn't* a problem, per se. Instead
there are places where several people can see room for improvement or where
they feel features would fit well(me and my list comprehension fetish, for
example). This leads to the issue of things that are nice to have, that may
well be noticeable improvements to the language and yet you still can't find
a valid arugment for supporting them. The best I can say is that I am
interested in exploring a clearer way to deal with ranges and lists.

As it stands, with the inclusion of generics I can only think of a single
real problem with the langauge, which is basically the general crappiness of
the casting operator.
At this point I'm starting to really fret. I don't know how to achieve a
complete and cohesive solution. Every time I get a set of rules working I
find a fundamental type that violates those rules. I'm having a tough
time
with strings now, a string is enumerable but will rarely be wanted to be
considered as such. I am probably going to have to define an override
operator(something that says "put this list in the list" instead of "put
this list's contents in the list", any recommendations on syntax?

Don't fret, just take a step back and clearly define the problem in terms
of
the end result. I have a pretty good idea of the end result I would like,


End results aren't the hard part. I have a pretty good grasp of the end
result and as much of a problem as one can muster here. The problem is every
practical way of tacking that result ends up having one showstopper issue or
another. Given the desire to maintain consistency, alot of rules have to be
considered and no one solution I've dreamt up yet maintains every rule and
still provides the feature set we are looking at. Many of the problems are
issues irregardless of if you use operators or comprehensions, the problem
comes in with lists, more specifically lists of lists, themselves.
but it doesn't necessarily mesh with all the other ideas being thrown
around. With that in mind, how about + to put a list in the list, and |
to
put the list's contents in the list w/ overlap and * to copy contents w/o
overlap... In that case:

[1..100] + [25..75] = [1..100, [25..75]]
[1..100] | [25..75] = [1..100, 25..75] // if this operator were
implemented
with overlap
[1..100] * [25..150] = [1..150] // if this operator were implemented with
no
overlap

How you would implement the list inside of a list, and what operator - if
any - to remove items from the list, is another story...

Thanks,
Michael C.


Nov 16 '05 #81

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:u7**************@tk2msftngp13.phx.gbl...
If other types implement these operators at some point, wouldn't it be
incumbent on the programmer to overload the operators if they wanted
different behavior?
Well, consider this:
[1...100] is an IList<int>.
[1...100] + [1...22] is effectivly IList<int> + IList<int>. As such a +
operator that operates on ILists is needed. However, if you provide a

class that implements IList<T> and overload operator+, what happens? I'm more
concerned about the general meaning than anything else. A psuedooperator
that adds lists adds all lists, I don't think its particularly clean to
limit it to one kind of list. Thus, you have potential conflicts.

Regardless of what operator you use, or how you implement, you have to
provide some base functionality for managing lists. If we take a step back
and look at <int> + <int> and the overloaded operator + in <string> +
<string>, we see that in comparison[list] +[list] should behave the same
whether we have a list of ints, strings, real numbers, enumerated types, or
anything we can compose a list of. If you think of the list as a basic
type, it becomes the programmer's responsibility to ensure proper
functionality if he decides to override the default operators. You can warn
programmers about the dangers, but you can't possibly account for every
potential (mis)-use of them.
Everyone's goals are a touch different. I'm pushing for a very abstract
concept where each individual syntax exists independently but can be
combined. I can't say for certain what exactly everyone else wants.
That's the starting point - deciding exactly what you are trying to achieve.
I agree that there is a need to be able to express these ideas. I see you
prefer operators while I prefer the comprehension style. I am concerned
about operator overloading issues as above, but my primary goal with
comprehensions is that they are more flexible(and probably less performant, bringing up your favorite adage again). A comprehension isn't limited to
joining, removing, and intersecting, but can be used to perform complex
operations on list contents, including transformations and lookups:
[yield GetValue(value) for 1...1000] for example;


True, I do prefer operators mostly because of their simplicity. But if
we're looking at list comprehensions, why re-invent the wheel from scratch?
Just borrow implementation details from the Python syntax. Of course then
we're talking about Python list objects and methods, which takes us back to
the efficiency and readability when converted to C#.

Thanks,
Michael C.
Nov 16 '05 #82
Well, consider this:
[1...100] is an IList<int>.
[1...100] + [1...22] is effectivly IList<int> + IList<int>. As such a +
operator that operates on ILists is needed. However, if you provide a class
that implements IList<T> and overload operator+, what happens? I'm more
concerned about the general meaning than anything else. A psuedooperator
that adds lists adds all lists, I don't think its particularly clean to
limit it to one kind of list. Thus, you have potential conflicts.


Regardless of what operator you use, or how you implement, you have to
provide some base functionality for managing lists. If we take a step
back
and look at <int> + <int> and the overloaded operator + in <string> +
<string>, we see that in comparison[list] +[list] should behave the same
whether we have a list of ints, strings, real numbers, enumerated types,
or
anything we can compose a list of. If you think of the list as a basic
type, it becomes the programmer's responsibility to ensure proper
functionality if he decides to override the default operators. You can
warn
programmers about the dangers, but you can't possibly account for every
potential (mis)-use of them.


I agree, however as it stands, lists *weren't* basic types at any point in
time. I suppose I could drop any semblence of compatibility with existing
code, but that makes the feature a bit less enticing.
Everyone's goals are a touch different. I'm pushing for a very abstract
concept where each individual syntax exists independently but can be
combined. I can't say for certain what exactly everyone else wants.
That's the starting point - deciding exactly what you are trying to
achieve.


I know what *I* want, its everyone else thats making life hard.
I agree that there is a need to be able to express these ideas. I see you
prefer operators while I prefer the comprehension style. I am concerned
about operator overloading issues as above, but my primary goal with
comprehensions is that they are more flexible(and probably less

performant,
bringing up your favorite adage again). A comprehension isn't limited to
joining, removing, and intersecting, but can be used to perform complex
operations on list contents, including transformations and lookups:
[yield GetValue(value) for 1...1000] for example;


True, I do prefer operators mostly because of their simplicity. But if
we're looking at list comprehensions, why re-invent the wheel from
scratch?
Just borrow implementation details from the Python syntax. Of course then
we're talking about Python list objects and methods, which takes us back
to
the efficiency and readability when converted to C#.

Thus far I've taken pythons implementation pretty much to heart. I changed
the syntax mainly because python's syntax exists in a pretty typeless world
and has a few bits that look terrible in C# and because python is pretty
typeless(or dynamically typed, or wahtever you want to call it), its hard to
deal with multiple lists with similar syntax. Right now my comprehension
syntax can only deal with *one* list. I wrote a syntax todeal with multiple
lists, but I didn't like it(shown below). Out side of that I've made no
changes.
python list comprehension is something like:
[x for x in 1...1000 if x%2==0]
while my suggestion is
[yield value for 1...1000 where value%2==0]
or
[1...1000 where value%2==0]
I chose where because it reads better without a yield. I also elected to use
value because everyone is used to value being implicitly typed, whereas
custom implicitly typed variables are vastly against the grain. However, it
does turn value into something more like a keyword than it is currently.

My inital multi list sequence was:
[yield new Pair(x,y) for x<- 1...1000, y<-2...2000]
but it has that nasty implicit typing again.
one other possibility may be multiple for's. Python apparently supports this
but it seems pretty esoteric.
[yield value for 1...1000 where value%2 == 0, for 2...2000 where value%2 ==
1] perhaps? I need to research this more.

However there is one issue .NET has that python was able to circumvent. In
python, strings don't appear to be enumerable objects(or atleast the
language can treat them as if they aren't), however in .NET they are. The
biggest problem is determining *what* a user means when they pass a string
to a list is hard. A string is both a basic type *and* a sequence of
characters. That is a huge wrench in the works right now.
Nov 16 '05 #83

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:en*************@TK2MSFTNGP10.phx.gbl...
python list comprehension is something like:
[x for x in 1...1000 if x%2==0]
while my suggestion is
[yield value for 1...1000 where value%2==0]
or
[1...1000 where value%2==0]


Most programmers already understand the for and foreach keywords, why
introduce unfamiliarity into a looping comprehension with 'yield'? How
about

[for value in 1..1000 where value%2 == 0]

or

[foreach value in ['a', 'b', 'c']]

The only 2 particular real problems I see (other than deciding what the
operators and delimiters should look like), are 1) dealing with lists inside
of lists and 2) lists composed of multiple types of values (i.e., [1, 'A',
3.141592, "hello"]). I don't see a problem with lists containing strings,
unless the user is trying to loop over a range of strings, as in
"abc".."xyz" in which case it would be hard to tell what the user wanted to
accomplish. In that instance, I would limit range declarations to simple
scalar types; although strings can be included in, and added to, lists, I
wouldn't allow strings to be used to declare ranges.

Cheers,
Michael C.
Nov 16 '05 #84

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:en*************@TK2MSFTNGP10.phx.gbl...
My inital multi list sequence was:
[yield new Pair(x,y) for x<- 1...1000, y<-2...2000]
but it has that nasty implicit typing again.
one other possibility may be multiple for's. Python apparently supports this but it seems pretty esoteric.
[yield value for 1...1000 where value%2 == 0, for 2...2000 where value%2 == 1] perhaps? I need to research this more.


[for x, y in [1..1000, -2..2000]]

Where the first variable in the for list loops the first range and the
second variable loops the inner second range. Again we have to explicitly
type x and y for this to work... Why not use for to loop ranges for scalar
types (as above), and foreach to iterate non-scalars (such as strings):

list = ["red", "green", "blue"];
[foreach color in list]

By explicit definition [for ... in ...] would loop scalars whereas [foreach
.... in ...] would iterate a list (scalar or non-scalar). The explicit
typing of the [for] would allow for greater optimization, while the implicit
typing (object?) of [foreach] would allow more flexibility. Of course you
could still use [foreach] to iterate a range of values, but the implicit
typing would mean a performance hit.
Nov 16 '05 #85

"Michael C" <no****@lol.net> wrote in message
news:N0*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:en*************@TK2MSFTNGP10.phx.gbl...
python list comprehension is something like:
[x for x in 1...1000 if x%2==0]
while my suggestion is
[yield value for 1...1000 where value%2==0]
or
[1...1000 where value%2==0]
Most programmers already understand the for and foreach keywords, why
introduce unfamiliarity into a looping comprehension with 'yield'? How
about

[for value in 1..1000 where value%2 == 0]

or

[foreach value in ['a', 'b', 'c']]


Because you lose the ability to do, say, return 2x value or return a string
based on the value.
[yield GetStringFor(value) for 1...1000].
Python allows an initial expression to do this, in the form
[GetStringFor(x) for x in list]
However, that simply doesn't make much sense in the C# way of things. Plus I
think yield will become much more commonly understood with the spead of C#
2.0. Plus, even though its not terribly apparent, a list comprehension is
really a very quick, easy to write iterator. Yield works with both, tying
the meaning of yield in quite nicely I think.
This also leaves both yield and where as context senstive words as they are
only valid within a comprehension.

I also don't think there is any value in using foreach here. Your foreach
example is *identical* to ['a','b','c'] and [['a','b','c']] due to the rule
I explain below. It might be sensible to replace for with foreach in all
examples and drop the for keyword from consideration. Again, however, I am
seriously concerned with implicit typing.
The only 2 particular real problems I see (other than deciding what the
operators and delimiters should look like), are 1) dealing with lists
inside
of lists and 2) lists composed of multiple types of values (i.e., [1, 'A',
3.141592, "hello"]). I don't see a problem with lists containing strings,
Dealing with multiple types basically forces the list to be typed object. It
would probably only be really valueable for reference based things however.
Dynamic languages certainly have the upper hand here
unless the user is trying to loop over a range of strings, as in
"abc".."xyz" in which case it would be hard to tell what the user wanted
to
accomplish. In that instance, I would limit range declarations to simple
scalar types; although strings can be included in, and added to, lists, I
wouldn't allow strings to be used to declare ranges.


One of the current rules in my lists spec is that IEnumerable objects are
expanded into the list. This rule allows the expression 1...1000 to return
an IEnumerable value(in most cases), which allows ranges to be used as
generic IEnumerables, both in foreach, as parameters, or as variables. It
also means you can construct a list from another list or other IEnumerable.
However, string happens to implement IEnumerable, so the syntax ["water"]
means literally the same as ['w','a','t','e','r'], which is unwarrented
*AND* unwanted in most, but probably not all, cases. Trying to make that fit
in is a rather difficult proposition. If the type was something *other* than
strings, I wouldn't have a problem introducing override syntax. However I
expect strings to be rather common and requring special syntax for strings
or using special rules is probably going to bite back rather quickly.

So, now the issue is figuring out how to express ranges legally and usefully
in the type system without comprimising something else or figure out how to
handle strings and contained lists.
Nov 16 '05 #86

"Michael C" <no****@lol.net> wrote in message
news:ba*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:en*************@TK2MSFTNGP10.phx.gbl...
My inital multi list sequence was:
[yield new Pair(x,y) for x<- 1...1000, y<-2...2000]
but it has that nasty implicit typing again.
one other possibility may be multiple for's. Python apparently supports this
but it seems pretty esoteric.
[yield value for 1...1000 where value%2 == 0, for 2...2000 where value%2

==
1] perhaps? I need to research this more.


[for x, y in [1..1000, -2..2000]]


What order does that return? all x's then all y's? or x, then y then x then
y and so on? How do you yield pairs? How do you perform joins?

Where the first variable in the for list loops the first range and the
second variable loops the inner second range. Again we have to explicitly
type x and y for this to work... Why not use for to loop ranges for
scalar
types (as above), and foreach to iterate non-scalars (such as strings):

list = ["red", "green", "blue"];
[foreach color in list]

By explicit definition [for ... in ...] would loop scalars whereas
[foreach
... in ...] would iterate a list (scalar or non-scalar). The explicit
typing of the [for] would allow for greater optimization, while the
implicit
typing (object?) of [foreach] would allow more flexibility. Of course you
could still use [foreach] to iterate a range of values, but the implicit
typing would mean a performance hit.
Again, I'm not sure if this really makes much sense. Each of these examples
is basically useless uses of the words. The for\foreach syntax doesn't add
anything to the list without some kind of processing.

Beyond that, since the lists are mostly strongly typed, I think you could
achieve optimizations using either for or foreach in the majority of cases,
even though it would include a sacrifice in clarity of the code to IL
mapping.

Nov 16 '05 #87

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#m*************@TK2MSFTNGP11.phx.gbl...
I also don't think there is any value in using foreach here. Your foreach
example is *identical* to ['a','b','c'] and [['a','b','c']] due to the rule I explain below. It might be sensible to replace for with foreach in all
examples and drop the for keyword from consideration. Again, however, I am
seriously concerned with implicit typing.

foreach would add value in cases where the range cannot be expanded to a
simple for loop and the items must be iterated over. Examples might be:

[foreach value in ["hello", 10, "how are you?", 'd', 3.141592]]

Obviously in this situation treating the values in the list like a range
makes no sense. You have to iterate over the 5 list items and return their
values one at a time... this comprises your looping in this instance. Also,
in this example implicit typing will cause a performance hit, but is really
the only way to accomplish the objective.
Dealing with multiple types basically forces the list to be typed object. It would probably only be really valueable for reference based things however. Dynamic languages certainly have the upper hand here

Well... if we're dealing with single-type lists, implicit typing should not
be an issue at all, as the reference variable should be explicitly typed
anyway. As in

int i;
[yield i for [1..1000]]
One of the current rules in my lists spec is that IEnumerable objects are
expanded into the list. This rule allows the expression 1...1000 to return
an IEnumerable value(in most cases), which allows ranges to be used as
generic IEnumerables, both in foreach, as parameters, or as variables.
Doesn't this mean that you'll later take a hit on optimization though? Like
optimizing loops? Also it will mean a good deal of memory dedicated to the
contents of large lists - [-32767..32767] will be a very large list indeed.
I don't have a particular problem with that, but how does enumerating the
individual items fit into your optimization scheme for looping? Or are you
still planning to do that?
However, string happens to implement IEnumerable, so the syntax ["water"]
means literally the same as ['w','a','t','e','r'], which is unwarrented
*AND* unwanted in most, but probably not all, cases. Trying to make that fit in is a rather difficult proposition. If the type was something *other* than strings, I wouldn't have a problem introducing override syntax. However I
expect strings to be rather common and requring special syntax for strings
or using special rules is probably going to bite back rather quickly.

Expanding the string "water" into 'w','a','t','e','r' in a list is probably
one of the most unnecessary things I've seen. Anyone who codes to that is
probably going to have a rude awakening if such an arcane feature
disappears. I don't see this as really being an issue. If you look at
ArrayLists, they implement IEnumerable, yet you can add strings to them and
retrieve them whole (not one char at a time) during the enumeration of the
ArrayList. I don't see how this violates anything, and I'm not sure how
implementing this same type of functionality in lists would be problematic.
If it does violate some rules of the road, at least Microsoft has set a
precendent for you with arrays, arraylists, and just about every other type
that can store strings and implements IEnumerable.
So, now the issue is figuring out how to express ranges legally and usefully in the type system without comprimising something else or figure out how to handle strings and contained lists.


From what you've said, it sounds like you already consider ranges to be
enumerations. And as I mentioned before, I would use ranges only where they
make sense - as shorthand for defining portions of the list. What areas
would this make sense in? Ints, Chars, Bytes, etc., etc. Basically any
type that has well-defined bounds and well-defined finite increments.
That's not to say you couldn't add other items to the list, but they would
need to be added on an individual basis. Ranges like ["hello".."cat"] and
[3.141592..2.8] make no sense to define as enumerable, incrementable ranges.
To add them to a list, however, [1..100, "hello", "cat", 3.141592, 2.8]
would make more sense, along with whatever in-between values the programmer
deems necessary. Again, you have to put some of the responsibility back on
the programmer. You can never write a tool that can be all things to all
people; but you can create tools that others can expand on later.

Thanks,
Michael C.
Nov 16 '05 #88

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:Og**************@tk2msftngp13.phx.gbl...
[for x, y in [1..1000, -2..2000]]
What order does that return? all x's then all y's? or x, then y then x

then y and so on? How do you yield pairs? How do you perform joins?

To my thinking, this would return an enumeration for x [1..1000] and a
nested loop of y [-2..2000]. The syntax is similar to Python but the
behavior I'd have to look up. Not sure if Python nests the y inside the x
or if it performs the y loop subsequently to the x loop.

Again, I'm not sure if this really makes much sense. Each of these examples is basically useless uses of the words. The for\foreach syntax doesn't add
anything to the list without some kind of processing.

The differentiation was strictly for your benefit - to ease your
implementation, since one requires strong typing and will let you know
upfront that it might be very optimizable; the other to let you know that
strong typing is not an option, and that optimization will not be able to be
as good. As long as you're willing to write the code to determine this
without the hints, you're good to go. It also appears that we're not as
concerned with optimization of for loops and typing - since lists will be
strongly typed. From what you've said, it appears that

[yield value in [1..10]]

is functionally and programmatically equivalent to

[yield value in [1,2,3,4,5,6,7,8,9,10]]

So that it cannot be optimized down to

for (value = 1; value <= 10; value++)

But rather something more like

foreach (value in new int [] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

Is this correct? If so, then the for/foreach examples I presented are
unnecessary. Just to make sure we're on the same page, we're no longer
concerned with optimization, right?
Beyond that, since the lists are mostly strongly typed, I think you could
achieve optimizations using either for or foreach in the majority of cases, even though it would include a sacrifice in clarity of the code to IL
mapping.


Well, again if we're dealing with strongly typed lists, then the why the
implicitly typed 'value' keyword? Just have the user declare a strongly
typed variable. It seems like we're bouncing back and forth on some of
these issues here. Do the lists have to be strongly typed, is implicit
typing an issue, what type of optimizations are you looking to implement?

The clarity of the code to IL mapping is probably not as critical to most
developers as it will be to you, since you're developing the actual tool.
As long as it doesn't cause you concern, it probably isn't too much of an
issue.

I guess one question I just have to ask is what exactly is a list? I've
worked with lists in various languages, and I'm viewing lists with the
preconceived notions here. Just for reference, how would *you* expand the
following statements out in C#?

List a = [1..100, 10..30, 500, 35];

List b = ["hello", "how are you?", "goodbye"];

[yield i in [1..100]]

[yield j in b]

And are you staying with strongly typed lists? If not, how would you
define, in C# terms, a list that is not strongly typed?

Thanks,
Michael C.
Nov 16 '05 #89

"Michael C" <no****@lol.net> wrote in message
news:DY*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#m*************@TK2MSFTNGP11.phx.gbl...
I also don't think there is any value in using foreach here. Your foreach
example is *identical* to ['a','b','c'] and [['a','b','c']] due to the rule
I explain below. It might be sensible to replace for with foreach in all
examples and drop the for keyword from consideration. Again, however, I
am
seriously concerned with implicit typing.


foreach would add value in cases where the range cannot be expanded to a
simple for loop and the items must be iterated over. Examples might be:

[foreach value in ["hello", 10, "how are you?", 'd', 3.141592]]

Obviously in this situation treating the values in the list like a range
makes no sense. You have to iterate over the 5 list items and return
their
values one at a time... this comprises your looping in this instance.
Also,
in this example implicit typing will cause a performance hit, but is
really
the only way to accomplish the objective.


Again, I see no reason why you can't merge this into one keyword. Two
different compiled representation of one keyword is irrelevent as long as
the *semantic* and effective behaviour is the same.
Dealing with multiple types basically forces the list to be typed object.

It
would probably only be really valueable for reference based things

however.
Dynamic languages certainly have the upper hand here


Well... if we're dealing with single-type lists, implicit typing should
not
be an issue at all, as the reference variable should be explicitly typed
anyway. As in

int i;
[yield i for [1..1000]]


IMHO, I'd like to find a way where the variable exists only *within* the
list, just as for and foreach loops work now.
One of the current rules in my lists spec is that IEnumerable objects are
expanded into the list. This rule allows the expression 1...1000 to
return
an IEnumerable value(in most cases), which allows ranges to be used as
generic IEnumerables, both in foreach, as parameters, or as variables.
Doesn't this mean that you'll later take a hit on optimization though?
Like
optimizing loops? Also it will mean a good deal of memory dedicated to
the
contents of large lists - [-32767..32767] will be a very large list
indeed.
I don't have a particular problem with that, but how does enumerating the
individual items fit into your optimization scheme for looping? Or are
you
still planning to do that?


This is where the compiler comes in. There are quite a few tricks that can
be pulled off by the compiler without the user being able to tell the
difference.
Consider these loops:

foreach (int i in 1...1000)
{

}

foreach (int i in [1...1000])
{

}

IEnumerable<int> enumer = 1...1000;
foreach (int i in enumer)
{

}

What do you expect to see? The compiler could easily transform the first two
into for loops while using the last as a literal enumeration(because it
can't guarentee that enumer is ranged). All of this is done based on
information the compiler has, specifically that in the first two cases you
are dealing with a generator and a list built off a generator, as such the
compiler can rather easily examine properties on the expression containing
high, low, and increment and generate the loop. The third, however, doesn't
exist as a range or list expression, it is instead a
LocalVariableExpression(or waht have you) which also resolves to
IEnumerable, but which cannot be optimzied.

The class in the compiler might be something like(discounting type's, which
complicates the compiler a bit)
class RangeExpression : Expression
{
public int UpperBound;
public int LowerBound;
public int Increment;
public Emit()
{
//generate a IEnumerable class *or* use a preexisting one
//create and load it.
}
}

and then the compiler could do
public void CompileForeach(Expression localExpression, Expression
enumeratorExpression)
{
....

if (expression is RangeExpression)
{
GenerateFor((RangeExpression)expression);
}
else
{
expression.Emit();
}
....
}
}

In esscense its entirely possible to optimize and still maintain 1...100 as
a enumerator. That particular magic is unsurprising, consisitent and
benificial, unlike some other bits.
It also makes this optimization optional, as not supporting it results in
code that is semantically identical.
However, string happens to implement IEnumerable, so the syntax ["water"]
means literally the same as ['w','a','t','e','r'], which is unwarrented
*AND* unwanted in most, but probably not all, cases. Trying to make that fit
in is a rather difficult proposition. If the type was something *other*

than
strings, I wouldn't have a problem introducing override syntax. However I
expect strings to be rather common and requring special syntax for
strings
or using special rules is probably going to bite back rather quickly.


Expanding the string "water" into 'w','a','t','e','r' in a list is
probably
one of the most unnecessary things I've seen. Anyone who codes to that is
probably going to have a rude awakening if such an arcane feature
disappears. I don't see this as really being an issue. If you look at
ArrayLists, they implement IEnumerable, yet you can add strings to them
and
retrieve them whole (not one char at a time) during the enumeration of the
ArrayList. I don't see how this violates anything, and I'm not sure how
implementing this same type of functionality in lists would be
problematic.
If it does violate some rules of the road, at least Microsoft has set a
precendent for you with arrays, arraylists, and just about every other
type
that can store strings and implements IEnumerable.


Its easier to do in ArrayList. ArrayList gives you a nice selection of
methods, like Add, which have a defined purpose. [] however has to have
semantics to deal with both sequences and single objects. Lists and strings,
along with other IEnumerable objects, are unfortunately both, so which
behavior do you apply to any given class?

If a list generated by [ ] can accept some enumerable expressions, it would
have to accept them all. I'm really unsure of how to approach this. If
1...1000 is an enumerable expression, then wouldn't you expect
IEnumerable<int> x = 1...1000
[x]
to mean the same as
[1...1000]?

following that logic,
List<int> x = new List<int>();
x.Add(1);
x.Add(2);
x.Add(3);
....
x.Add(1000);
[x]

should mean the same shouldn't it?

what about
[GetRangeSequence(1,1000)]

public IEnumerable<int> GetRangeSequence(int start, int finish)
{
for (int i = start; i <= finish; i++)
yield return i;

}

should that result in the same list?

if you say yes to all of these, then why wouldn't
["water"]
and
['w','a','t','e','r']
be the same?
So, now the issue is figuring out how to express ranges legally and usefully
in the type system without comprimising something else or figure out how

to
handle strings and contained lists.


From what you've said, it sounds like you already consider ranges to be
enumerations. And as I mentioned before, I would use ranges only where
they
make sense - as shorthand for defining portions of the list. What areas
would this make sense in? Ints, Chars, Bytes, etc., etc. Basically any
type that has well-defined bounds and well-defined finite increments.
That's not to say you couldn't add other items to the list, but they would
need to be added on an individual basis. Ranges like ["hello".."cat"] and
[3.141592..2.8] make no sense to define as enumerable, incrementable
ranges.
To add them to a list, however, [1..100, "hello", "cat", 3.141592, 2.8]
would make more sense, along with whatever in-between values the
programmer
deems necessary. Again, you have to put some of the responsibility back
on
the programmer. You can never write a tool that can be all things to all
people; but you can create tools that others can expand on later.


I think you miss the point. The issue isn't that strings wn't work in
ranges, they certainly don't make sense in them. The point is that a string
is indistiguishable from any other range when viewed through IEnumerable
colored glasses.
Thanks,
Michael C.

Nov 16 '05 #90

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#x**************@TK2MSFTNGP10.phx.gbl...
Again, I see no reason why you can't merge this into one keyword. Two
different compiled representation of one keyword is irrelevent as long as
the *semantic* and effective behaviour is the same.
See other post. As long as you're willing to differentiate between weakly
typed lists and strongly typed lists; or prohibit weakly typed lists
altogether; and don't care about optimization hints, it isn't necessary and
one keyword is just fine.

Well... if we're dealing with single-type lists, implicit typing should
not
be an issue at all, as the reference variable should be explicitly typed
anyway. As in

int i;
[yield i for [1..1000]]


IMHO, I'd like to find a way where the variable exists only *within* the
list, just as for and foreach loops work now.


Simple.

[yield int i for [1..1000]]

Since it's declared within the scope of the list iterator, it exists only
within that scope just like a for loop.

This is where the compiler comes in. There are quite a few tricks that can
be pulled off by the compiler without the user being able to tell the
difference.
Consider these loops: ...
In esscense its entirely possible to optimize and still maintain 1...100 as a enumerator. That particular magic is unsurprising, consisitent and
benificial, unlike some other bits.
It also makes this optimization optional, as not supporting it results in
code that is semantically identical.

This makes perfect sense when dealing with ranges and lists composed only of
ranges. Just one question - how would it handle the following:

[1..10]
[1,2,3,4,5,6,7,8,9,10]

If a list generated by [ ] can accept some enumerable expressions, it would have to accept them all. I'm really unsure of how to approach this. If
1...1000 is an enumerable expression, then wouldn't you expect
IEnumerable<int> x = 1...1000
[x]
to mean the same as
[1...1000]?

following that logic,
List<int> x = new List<int>();
x.Add(1);
x.Add(2);
x.Add(3); ....
should that result in the same list?

if you say yes to all of these, then why wouldn't
["water"]
and
['w','a','t','e','r']
be the same?
For one, "water" is not, by definition, a range. Since we're talking about
expected results here, do you really expect that "water" should present an
enumerable Range of values like 1..1000? Or that List.Add("water") should
produce the same result as

List.Add('w');
List.Add('a');
List.Add('t');
List.Add('e');
List.Add('r');

I think the difference we're talking about here is the difference between
enumerable objects and enumerable Ranges. By definition, I would think that
a Range is composed of objects, and that the enumeration of a List would not
automatically break down and enumerate string objects in the List. Although
if the user wanted to address the individual list objects, they could
obviously enumerate them... Themselves, and one at a time.
I think you miss the point. The issue isn't that strings wn't work in
ranges, they certainly don't make sense in them. The point is that a string is indistiguishable from any other range when viewed through IEnumerable
colored glasses.


See, difference of opinion - I think you miss the point.

Again, I go back to ArrayLists. ArrayLists are IEnumerable. You can add
strings to them. The ArrayList, during enumeration does not break down the
string into its component chars. So why is the eyeglass prescription so
different for a List when compared to other IEnumerables?

Especially since, unlike ArrayList, we're looking at *strongly* typed
lists... It's not like we're going to enumerate a list and hit <int>,
<int>, <string>... You know in advance whether it's a list of strings or a
list of ints, right? With that knowledge, why would enumerating a
strongly-typed list of strings be a problem? Especially since we already
know in advance that we can't define a range of strings; so we know in
advance that if we have a list of strings we're not going to enumerate them
into their individual chars or bits or other weirdness...
Nov 16 '05 #91

"Michael C" <no****@lol.net> wrote in message
news:%j*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:Og**************@tk2msftngp13.phx.gbl...
> [for x, y in [1..1000, -2..2000]]
What order does that return? all x's then all y's? or x, then y then x

then
y and so on? How do you yield pairs? How do you perform joins?


To my thinking, this would return an enumeration for x [1..1000] and a
nested loop of y [-2..2000]. The syntax is similar to Python but the
behavior I'd have to look up. Not sure if Python nests the y inside the x
or if it performs the y loop subsequently to the x loop.

Again, I'm not sure if this really makes much sense. Each of these

examples
is basically useless uses of the words. The for\foreach syntax doesn't
add
anything to the list without some kind of processing.


The differentiation was strictly for your benefit - to ease your
implementation, since one requires strong typing and will let you know
upfront that it might be very optimizable; the other to let you know that
strong typing is not an option, and that optimization will not be able to
be
as good. As long as you're willing to write the code to determine this
without the hints, you're good to go. It also appears that we're not as


I think determining the two *shouldn't* be too terribly difficult.
Implementation is usually the easy part, its defining the proper behaviour
and making it work with every other rule that you have to abide by that will
make you tear your hair out and keep it in a rasin canister.

Thanks for the thought though. concerned with optimization of for loops and typing - since lists will be
strongly typed. From what you've said, it appears that

[yield value in [1..10]]

is functionally and programmatically equivalent to

[yield value in [1,2,3,4,5,6,7,8,9,10]]

So that it cannot be optimized down to

for (value = 1; value <= 10; value++)

But rather something more like

foreach (value in new int [] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10})

Is this correct? If so, then the for/foreach examples I presented are
unnecessary. Just to make sure we're on the same page, we're no longer
concerned with optimization, right?

They are both semantically identical; that is they *will* have the same end
result. However the actual implementation and function of the two may be
*very* different and the compiler itself will certainly be able to know the
difference. Since ranges have a defined start and end bound in many cases,
it wouldn't be hard for the compiler to play tricks and efficently generate
lists that take up very little space until they are actually used by
reserving indexes that exist as an enumerator but not actually generating
the values until they are accessed. A list comprehension is a different
matter and will usually result in a completely generated list since in most
any circumstance you use a where expression the ability to determine range
length goes out the window. It wouldn't be impossible for the compiler to
evaluate mathematic only where expression for ints and other baisc types and
determine the actual number of results, I'm just not sure I would want to go
that far unless it showed significant value.

It also wouldn't be beyond the compiler to take expressions like
[1,2,3,4,5,6,7,8,9,10] and transform them into 1...10 (or vice versa) if it
felt execution or memory efficency would benifit. For exceedingly small
cases like this, I would probably lean towards generating
[1,2,3,4,5,6,7,8,9,10] instead of forcing generation of each value.
Similarly if someone was insane enough to put 800000 direct sequential
entries of longs into a list I would think collapsing it into a range would
be better.

A pragma is probably appropriate to explicitly enable or disable this type
of optimization over a range, atleast for the proof of concept
implementation. It'd make testing easier and help out if there is a
particualr reason you want one or the other that the compiler can't
understand(like a massive number of random index lookups).
Beyond that, since the lists are mostly strongly typed, I think you could
achieve optimizations using either for or foreach in the majority of

cases,
even though it would include a sacrifice in clarity of the code to IL
mapping.


Well, again if we're dealing with strongly typed lists, then the why the
implicitly typed 'value' keyword? Just have the user declare a strongly
typed variable. It seems like we're bouncing back and forth on some of
these issues here. Do the lists have to be strongly typed, is implicit
typing an issue, what type of optimizations are you looking to implement?

The clarity of the code to IL mapping is probably not as critical to most
developers as it will be to you, since you're developing the actual tool.
As long as it doesn't cause you concern, it probably isn't too much of an
issue.

I guess one question I just have to ask is what exactly is a list? I've
worked with lists in various languages, and I'm viewing lists with the
preconceived notions here. Just for reference, how would *you* expand the
following statements out in C#?

Each of these is under my desires as well as under what the rules *would* do
in the compiler:
my intent first, actual effective results second List a = [1..100, 10..30, 500, 35]; literally, not going to type everything out: what is typed
values 1 through 100, then 10 through 30, 500, and 35
same for the compiler
List b = ["hello", "how are you?", "goodbye"];
As it is here: hello, how are you?, and goodbye.
however, based on range rules the compier would generate each cahracter
seperately. Hopefully I explained that well enough in my other post. [yield i in [1..100]] 1 through 100
[yield j in b] If you follow my rules you get: hello, how are you? and goodbye.
if you follow the actual compiler rules you end up with each character
because b is already a list of each character.

This is where my issue is, creating rules for the compiler that will
evaluate properly, not nessecerily creating a working model in my mind.

And are you staying with strongly typed lists? If not, how would you
define, in C# terms, a list that is not strongly typed? A list will be strongly typed where possible. However I'm implicitly typing
value because I don't want to have to type int i in a comprehension:
[yield i foreach int i in 1...1000]
isn't terribly attractive, although it is effective and perhaps nessecery.
Value is already implicitly typed in properties, indexers, etc.

The list [1...1000] *will* evaluate to IList<int>
the list [1D...1000D] will evaluate to IList<double>
etc.

Thanks,
Michael C.

Nov 16 '05 #92

"Michael C" <no****@lol.net> wrote in message
news:vK*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#x**************@TK2MSFTNGP10.phx.gbl...
Again, I see no reason why you can't merge this into one keyword. Two
different compiled representation of one keyword is irrelevent as long as
the *semantic* and effective behaviour is the same.
See other post. As long as you're willing to differentiate between weakly
typed lists and strongly typed lists; or prohibit weakly typed lists
altogether; and don't care about optimization hints, it isn't necessary
and
one keyword is just fine.
>
> Well... if we're dealing with single-type lists, implicit typing should
> not
> be an issue at all, as the reference variable should be explicitly
> typed
> anyway. As in
>
> int i;
> [yield i for [1..1000]]


IMHO, I'd like to find a way where the variable exists only *within* the
list, just as for and foreach loops work now.


Simple.

[yield int i for [1..1000]]

Since it's declared within the scope of the list iterator, it exists only
within that scope just like a for loop.


That sitll doesn't explain how to return i*2.
[yield int i*2 for[1...1000]] doesn't make a whole lot of sense.

This is where the compiler comes in. There are quite a few tricks that
can
be pulled off by the compiler without the user being able to tell the
difference.
Consider these loops: ...
In esscense its entirely possible to optimize and still maintain 1...100 as
a enumerator. That particular magic is unsurprising, consisitent and
benificial, unlike some other bits.
It also makes this optimization optional, as not supporting it results in
code that is semantically identical.


This makes perfect sense when dealing with ranges and lists composed only
of
ranges. Just one question - how would it handle the following:

[1..10]
[1,2,3,4,5,6,7,8,9,10]


Explained that in the other post, we should probably merge these into one
sometime soon before things get confusing, LOL.

If a list generated by [ ] can accept some enumerable expressions, it would
have to accept them all. I'm really unsure of how to approach this. If
1...1000 is an enumerable expression, then wouldn't you expect
IEnumerable<int> x = 1...1000
[x]
to mean the same as
[1...1000]?

following that logic,
List<int> x = new List<int>();
x.Add(1);
x.Add(2);
x.Add(3);

...

should that result in the same list?

if you say yes to all of these, then why wouldn't
["water"]
and
['w','a','t','e','r']
be the same?


For one, "water" is not, by definition, a range. Since we're talking
about
expected results here, do you really expect that "water" should present an
enumerable Range of values like 1..1000? Or that List.Add("water") should
produce the same result as

List.Add('w');
List.Add('a');
List.Add('t');
List.Add('e');
List.Add('r');

I think the difference we're talking about here is the difference between
enumerable objects and enumerable Ranges. By definition, I would think
that
a Range is composed of objects, and that the enumeration of a List would
not
automatically break down and enumerate string objects in the List.
Although
if the user wanted to address the individual list objects, they could
obviously enumerate them... Themselves, and one at a time.


Howeve,r you miss a point. List.Add *doesn't* support adding ranges in any
way, shape, or form. *EVERYTHING* is a single object. If List.Add did double
duty, adding both ranges and single items you would probably have a similar
problem(the compiler choosing Add(IEnumerable) over Add(object) when you
pass it a string). It *NEVER* has to make a descision about what to do, its
behaviour is well defined and mapped out. As things stand with this list
syntax there is no clear definition that results in expected behaviour.

Also, to make a point, what I expect isn't the issue. I certainly don't want
strings to behave this way. The problem is I can't find a set of rules that
does what I do want *without* causing this behaviour in strings.

The issue really is, what is a range? Is a range simply a shortcut that
makes lists and some loops easier or is it a general purpose construct that
expresses a range of values? If its hte former you can throw this all away
and end up with significantly less flexibility, if its the former you have
to find a set of rules taht allows its flexibility without compromising
anything else.
I think you miss the point. The issue isn't that strings wn't work in
ranges, they certainly don't make sense in them. The point is that a

string
is indistiguishable from any other range when viewed through IEnumerable
colored glasses.


See, difference of opinion - I think you miss the point.

Again, I go back to ArrayLists. ArrayLists are IEnumerable. You can add
strings to them. The ArrayList, during enumeration does not break down
the
string into its component chars. So why is the eyeglass prescription so
different for a List when compared to other IEnumerables?

Especially since, unlike ArrayList, we're looking at *strongly* typed
lists... It's not like we're going to enumerate a list and hit <int>,
<int>, <string>... You know in advance whether it's a list of strings or
a
list of ints, right? With that knowledge, why would enumerating a
strongly-typed list of strings be a problem? Especially since we already
know in advance that we can't define a range of strings; so we know in
advance that if we have a list of strings we're not going to enumerate
them
into their individual chars or bits or other weirdness...


You know ahead of time while enumerating, but the lists type is implicitly
determined from its contents. Just as the compiler infers that 1 is an
integer in most cases, it would infer that [1] is a list of integers. Thus,
the compiler decides what type the list is based on the contents of the
list, and thus it can't use the type of the list to determine how to use the
contents of said list.

This is a departure from standard C# behaviour, where everything is
explicitly typed, but I can't think of a really good way to do it any other
way. Ideally the object would support both IList<T> and IList<object> where
applicable, but that really is a different matter altogether.

Now, I'll give you taht you could explicitly ignore string when processing
IEnumerables, however that breaks consistency. Instead of saying "any
IEnumerable expression", it becomes "any IEnumerable expression except those
typed string" which is tricky, IMHO.
Nov 16 '05 #93

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#L**************@TK2MSFTNGP09.phx.gbl...
Simple.

[yield int i for [1..1000]]

Since it's declared within the scope of the list iterator, it exists only within that scope just like a for loop.
That sitll doesn't explain how to return i*2.
[yield int i*2 for[1...1000]] doesn't make a whole lot of sense.


It makes as much sense as [yield value*2 for [1..1000]]. I guess the value
of "value" is only valid within the [ ]'s? So how will you, for instance,
print 'value' to the console? Or use 'value' in a conditional statement or
anything else?

ranges. Just one question - how would it handle the following:

[1..10]
[1,2,3,4,5,6,7,8,9,10]


Explained that in the other post, we should probably merge these into one
sometime soon before things get confusing, LOL.


Agreed. LOL.
Also, to make a point, what I expect isn't the issue. I certainly don't want strings to behave this way. The problem is I can't find a set of rules that does what I do want *without* causing this behaviour in strings.

On a purely physical level, we can say the difference between ranges and
other types is the .. operator for starters. Obviously if the .. operator
is not there, it's not a range. By adding ranges, the compiler *will* be
making a choice about adding ranges or adding single objects, correct?
So... if we are not dealing with a range, we obviously add a single
object... whether string or int or whatever...
The issue really is, what is a range? Is a range simply a shortcut that
makes lists and some loops easier or is it a general purpose construct that expresses a range of values? If its hte former you can throw this all away
and end up with significantly less flexibility, if its the former you have
to find a set of rules taht allows its flexibility without compromising
anything else.

I agree with the flexibility, but I also think there should be some level of
*consistency* in the user experience when designing a solution. For
instance, based on my experience with other IEnumerables, I would not expect
the following:

List.Add("water");
[yield value in List]

To return

w
a
t
e
r

This is based on my experience with other IEnumerables where you can expect
the following:

myArrayList.Add("water");
foreach (s in myArrayList)
Console.WriteLine (s);

Returns

water

Whatever rules you decide to implement, in whatever fashion, I would just
try to make sure that they provide consistency with other comparable types.

You know ahead of time while enumerating, but the lists type is implicitly
determined from its contents. Just as the compiler infers that 1 is an
integer in most cases, it would infer that [1] is a list of integers. Thus, the compiler decides what type the list is based on the contents of the
list, and thus it can't use the type of the list to determine how to use the contents of said list.

OK, you lost me here. The compiler *can* determine the type of the list,
yet it *can't* use that information? Think about that... It just makes no
sense at all.
Now, I'll give you taht you could explicitly ignore string when processing
IEnumerables, however that breaks consistency. Instead of saying "any
IEnumerable expression", it becomes "any IEnumerable expression except those typed string" which is tricky, IMHO.


Now we get to the core... It's tricky. We know how we think it should
operate; and I think you might agree that a string within a list should be
returned as a complete string when the list is enumerated. So the only
major sticking point is the trickiness.

As for consistency, IMHO, the main consistency issue that needs to be
addressed is consistency in the overall user experience. If the average
user would expect that adding a string to a list would result in that entire
string being returned - intact - during enumeration of the list, then that's
what should be implemented; regardless of any esoteric rules regarding
differentiation of IEnumerables. Perhaps taking another step back, the
major issue here seems to be Ranges and their conflict with strings/ Should
we do as you said above and re-think the definition of a range? It seems a
range should be an IEnumerable type of its own, in which case you need to
check for IEnumerable Ranges versus other types and treat them differently,
as opposed to treating strings differently.
Nov 16 '05 #94

"Michael C" <no****@lol.net> wrote in message
news:yh*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#L**************@TK2MSFTNGP09.phx.gbl...
> Simple.
>
> [yield int i for [1..1000]]
>
> Since it's declared within the scope of the list iterator, it exists only > within that scope just like a for loop.
That sitll doesn't explain how to return i*2.
[yield int i*2 for[1...1000]] doesn't make a whole lot of sense.


It makes as much sense as [yield value*2 for [1..1000]]. I guess the
value
of "value" is only valid within the [ ]'s? So how will you, for instance,
print 'value' to the console? Or use 'value' in a conditional statement
or
anything else?


For more complicated situations like that, a foreach loop makes *alot* more
sense. List comprehensions are basically for simple list transformations and
to express more complicated ranges succinctly.
> ranges. Just one question - how would it handle the following:
>
> [1..10]
> [1,2,3,4,5,6,7,8,9,10]
Explained that in the other post, we should probably merge these into one
sometime soon before things get confusing, LOL.


Agreed. LOL.
Also, to make a point, what I expect isn't the issue. I certainly don't

want
strings to behave this way. The problem is I can't find a set of rules

that
does what I do want *without* causing this behaviour in strings.


On a purely physical level, we can say the difference between ranges and
other types is the .. operator for starters. Obviously if the .. operator
is not there, it's not a range. By adding ranges, the compiler *will* be
making a choice about adding ranges or adding single objects, correct?
So... if we are not dealing with a range, we obviously add a single
object... whether string or int or whatever...
The issue really is, what is a range? Is a range simply a shortcut that
makes lists and some loops easier or is it a general purpose construct

that
expresses a range of values? If its hte former you can throw this all
away
and end up with significantly less flexibility, if its the former you
have
to find a set of rules taht allows its flexibility without compromising
anything else.


I agree with the flexibility, but I also think there should be some level
of
*consistency* in the user experience when designing a solution. For
instance, based on my experience with other IEnumerables, I would not
expect
the following:

List.Add("water");
[yield value in List]

To return

w
a
t
e
r


Nor would I, nor would it really.
This is the tricy part, while
List.Add("water");
[yield value in List]

will return a list with one value, that being "water",
List = ["water"]
[yield value in List]
would result in
w
a
t
e
r

It has nothing to do with enumeration as such, it is entirely based in
interpreting list items. How do you interpret ["abc"]. Is it a string or is
it a IEnumerable object.

You know ahead of time while enumerating, but the lists type is
implicitly
determined from its contents. Just as the compiler infers that 1 is an
integer in most cases, it would infer that [1] is a list of integers.

Thus,
the compiler decides what type the list is based on the contents of the
list, and thus it can't use the type of the list to determine how to use

the
contents of said list.


OK, you lost me here. The compiler *can* determine the type of the list,
yet it *can't* use that information? Think about that... It just makes
no
sense at all.


Well, I guess I should rephrase that: Since the compiler uses the type of
its entries to infer the type of the list, the type of the list cannot be
used to infer the type of the entries.
In other words, you can't decide whether ["abc"] is a list of three
characters or a list of one string based on the list being typed string or
character because its recursive, the list type is based on the
interpretation of the type you need the list type to interpret.
Now, I'll give you taht you could explicitly ignore string when
processing
IEnumerables, however that breaks consistency. Instead of saying "any
IEnumerable expression", it becomes "any IEnumerable expression except

those
typed string" which is tricky, IMHO.


Now we get to the core... It's tricky. We know how we think it should
operate; and I think you might agree that a string within a list should be
returned as a complete string when the list is enumerated. So the only
major sticking point is the trickiness.

As for consistency, IMHO, the main consistency issue that needs to be
addressed is consistency in the overall user experience. If the average
user would expect that adding a string to a list would result in that
entire
string being returned - intact - during enumeration of the list, then
that's
what should be implemented; regardless of any esoteric rules regarding
differentiation of IEnumerables. Perhaps taking another step back, the
major issue here seems to be Ranges and their conflict with strings/
Should
we do as you said above and re-think the definition of a range? It seems
a
range should be an IEnumerable type of its own, in which case you need to
check for IEnumerable Ranges versus other types and treat them
differently,
as opposed to treating strings differently.

Nov 16 '05 #95

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#g*************@TK2MSFTNGP11.phx.gbl...
It makes as much sense as [yield value*2 for [1..1000]]. I guess the
For more complicated situations like that, a foreach loop makes *alot*

more sense. List comprehensions are basically for simple list transformations and to express more complicated ranges succinctly.
But even in a situation like this, where I simply want to write the value*2
to the screen, where do I put the Console.WriteLine (value) at in this
statement:

[yield value*2 for [1..1000]]
It has nothing to do with enumeration as such, it is entirely based in
interpreting list items. How do you interpret ["abc"]. Is it a string or is it a IEnumerable object.

Well, for our purposes it's obviously a string. And a string, while
IEnumerable, must be explicitly enumerated, no? We're obviously not
explicitly enumerating it; it seems to have more to do with setting up
special handling for Ranges versus other objects, since Ranges are really
the only IEnumerables we want to implicitly enumerate in the list.
Well, I guess I should rephrase that: Since the compiler uses the type of
its entries to infer the type of the list, the type of the list cannot be
used to infer the type of the entries.
In other words, you can't decide whether ["abc"] is a list of three
characters or a list of one string based on the list being typed string or
character because its recursive, the list type is based on the
interpretation of the type you need the list type to interpret.


I haven't actually built a compiler since I made a small C compiler back in
college, but this doesn't seem to follow. The compiler knows whether it is
a list of strings or list of chars, if for no other reason, the " and '.
Based on that, you could, if you were so inclined, create a special case for
strings. I understand you don't want to do that, but you have to actually
create a special case at some point. You automatically, by definition, have
two special cases:

1. List Items that should be implicitly enumerated when the list is
enumerated, and
2. List Items that should not be implicitly enumerated when the list is
enumerated

List Items that would fit into case 1 would include Ranges (and if you were
so inclined to add them later, sub-lists, arrays, etc.) List Items that
would fit into case 2 would include ints, chars, strings, etc. The
difference is you have to look at Ranges as objects unto themselves which
can be components in the list.
Nov 16 '05 #96

"Michael C" <no****@lol.net> wrote in message
news:%D*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#g*************@TK2MSFTNGP11.phx.gbl...
> It makes as much sense as [yield value*2 for [1..1000]]. I guess the
For more complicated situations like that, a foreach loop makes *alot*

more
sense. List comprehensions are basically for simple list transformations

and
to express more complicated ranges succinctly.


But even in a situation like this, where I simply want to write the
value*2
to the screen, where do I put the Console.WriteLine (value) at in this
statement:

[yield value*2 for [1..1000]]


Hrmm, I think that it shouldn't be possible. List comprehensions are
supposed to provide list transformations, that is converting one list into
another based on simple rules. I don't know if it is appropriate for general
iteration syntax.
I'd rather see
foreach (int i in [yield value*2 in 1...1000])
Console.WriteLine(i);
It has nothing to do with enumeration as such, it is entirely based in
interpreting list items. How do you interpret ["abc"]. Is it a string or is
it a IEnumerable object.


Well, for our purposes it's obviously a string. And a string, while
IEnumerable, must be explicitly enumerated, no? We're obviously not
explicitly enumerating it; it seems to have more to do with setting up
special handling for Ranges versus other objects, since Ranges are really
the only IEnumerables we want to implicitly enumerate in the list.


Well, there are other lists and array's and possibly the odd iterator or
two.
Well, I guess I should rephrase that: Since the compiler uses the type of
its entries to infer the type of the list, the type of the list cannot be
used to infer the type of the entries.
In other words, you can't decide whether ["abc"] is a list of three
characters or a list of one string based on the list being typed string
or
character because its recursive, the list type is based on the
interpretation of the type you need the list type to interpret.


I haven't actually built a compiler since I made a small C compiler back
in
college, but this doesn't seem to follow. The compiler knows whether it
is
a list of strings or list of chars, if for no other reason, the " and '.


Well, I think maybe I misunderstood your original intent. I thought you had
implied that the compiler should decide on the interpretation of string
based on the list type, which isn't possible because the lists type depends
on how you interpert the string. In light of all your other comments I
suspect you were implying something different.
Based on that, you could, if you were so inclined, create a special case
for
strings. I understand you don't want to do that, but you have to actually
create a special case at some point. You automatically, by definition,
have
two special cases:

1. List Items that should be implicitly enumerated when the list is
enumerated, and
2. List Items that should not be implicitly enumerated when the list is
enumerated (minor disagreement here, I'm concerned with list generation, not
enumeration. By the time enumeration rolls around this should all have been
sorted out).
List Items that would fit into case 1 would include Ranges (and if you
were
so inclined to add them later, sub-lists, arrays, etc.) List Items that
would fit into case 2 would include ints, chars, strings, etc. The
difference is you have to look at Ranges as objects unto themselves which
can be components in the list. Right. There are there goals I'm working towards:
1) allow ranges in lists
2) allow ranges to be placed in variables.
3) allow variables holding ranges to be loaded into lists
4) allow sub lists, arrays, etc to be added to lists.
5?) allow lists to be added to lists?

ICollection could be used to express all of these with out matching strings,
which makes it look idea. But that still takes away the ability to create
lists of arrays or other lists. At the least one would want to be able to
define
list = [1,2,3];
list =[list,4];
right?
Maybe ICollection *is* better. Lists of lists could be constrained by using
a syntax that explicitly marks an ICollection as a single object, not as a
list to enumerate?

Nov 16 '05 #97

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:un**************@TK2MSFTNGP09.phx.gbl...

Hrmm, I think that it shouldn't be possible. List comprehensions are
supposed to provide list transformations, that is converting one list into
another based on simple rules. I don't know if it is appropriate for general iteration syntax.
I'd rather see
foreach (int i in [yield value*2 in 1...1000])
Console.WriteLine(i);

That's perfect. I was just wondering how we would be able to use the values
yielded by the list comprehensions in code. In this case, I rather prefer
your previous recommendation of using [yield value...from...], as in is
already a reserved keyword and the 2 in's almost back to back does look a
little funny. But that's just a little thought on the cosmetics and not all
that important really.

Well, there are other lists and array's and possibly the odd iterator or
two.

True, I was referring to strings as the primary sticking point we're
discussing - the strings being IEnumerable objects that we don't want to
implicitly enumerate.

Well, I think maybe I misunderstood your original intent. I thought you had implied that the compiler should decide on the interpretation of string
based on the list type, which isn't possible because the lists type depends on how you interpert the string. In light of all your other comments I
suspect you were implying something different.
True, I was thinking the compiler would know the type of list based on the
contents of the list; and I was thinking it should determine a path of
action based on that information. Most cases I would think could be broken
down into two paths depending on the type of list enumerated: Lists
composed of objects we want to implicitly enumerate should be enumerated
(arrays, lists, ranges, etc.); Lists composed of objects we don't want to
implicitly enumerate should not be implicitly enumerated when the list ie
enumerated (strings, ints, etc). I know it makes it more complex to have
two separate paths of action, but I don't see any other particular way to
handle it...
(minor disagreement here, I'm concerned with list generation, not
enumeration. By the time enumeration rolls around this should all have been sorted out).
It does depend on how we're storing the list. This will sort itself out
only if we expand the list into it's composite objects at list generation
time, so that:

aList = [1..4] -> converted to aList = [1,2,3,4] at list generation time and
stored as 4 separate comoposite objects at generation

Alternatively, I thought we were looking to generate the list above as:

aList = [1..4] -> converted to aList = [Range(1..4)] so that the single
composite object Range would be stored

If this is the case then we have one action path at list generation and 2
separate action paths at list enumeration time, based on each type of
object.
Right. There are there goals I'm working towards:
1) allow ranges in lists
2) allow ranges to be placed in variables.
3) allow variables holding ranges to be loaded into lists
If we define Ranges as a type, we should be able to do this without much
trouble.
4) allow sub lists, arrays, etc to be added to lists.
5?) allow lists to be added to lists?
A question about adding arrays, lists, ranges - In the following:

Range = 1..5;
List = [Range];
Range = 6..10;

The contents of the List would be populated via the Ranges ICloneable
interface? So that List would not change, or does the List actually contain
a reference to Range (in which case the contents of List would be [6..10]
after Range is changed)?
Maybe ICollection *is* better. Lists of lists could be constrained by using a syntax that explicitly marks an ICollection as a single object, not as a
list to enumerate?


The ICollection does look like it might be a way to go. I think that's more
of the direction I'm leaning - a single List object composed of other single
objects (Range objects, string objects, list objects, chars, ints, etc.)
Nov 16 '05 #98

"Michael C" <no****@lol.net> wrote in message
news:Mk*********************@news4.srv.hcvlny.cv.n et...

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:un**************@TK2MSFTNGP09.phx.gbl...

Hrmm, I think that it shouldn't be possible. List comprehensions are
supposed to provide list transformations, that is converting one list
into
another based on simple rules. I don't know if it is appropriate for general
iteration syntax.
I'd rather see
foreach (int i in [yield value*2 in 1...1000])
Console.WriteLine(i);


That's perfect. I was just wondering how we would be able to use the
values
yielded by the list comprehensions in code. In this case, I rather prefer
your previous recommendation of using [yield value...from...], as in is
already a reserved keyword and the 2 in's almost back to back does look a
little funny. But that's just a little thought on the cosmetics and not
all
that important really.


Ya, I was tired when I responded. That should have been [yield value*2 for
1...1000]. Comprehensions always generate a new list, with all the benifits
and drawbacks of that. You can iterate over it or whatever else you wish to
do with that particularly typed list.

Note that back to back in's is a big reason why I was pushing for isin in
another portion of the thread.

Well, there are other lists and array's and possibly the odd iterator or
two.

True, I was referring to strings as the primary sticking point we're
discussing - the strings being IEnumerable objects that we don't want to
implicitly enumerate.


Ya, I just don't like arbitrarily choosing which the compiler will handle.
It ends up being to much like compiler magic and will probably confuse
users.

Well, I think maybe I misunderstood your original intent. I thought you

had
implied that the compiler should decide on the interpretation of string
based on the list type, which isn't possible because the lists type

depends
on how you interpert the string. In light of all your other comments I
suspect you were implying something different.


True, I was thinking the compiler would know the type of list based on the
contents of the list; and I was thinking it should determine a path of
action based on that information. Most cases I would think could be
broken
down into two paths depending on the type of list enumerated: Lists
composed of objects we want to implicitly enumerate should be enumerated
(arrays, lists, ranges, etc.); Lists composed of objects we don't want to
implicitly enumerate should not be implicitly enumerated when the list ie
enumerated (strings, ints, etc). I know it makes it more complex to have
two separate paths of action, but I don't see any other particular way to
handle it...
(minor disagreement here, I'm concerned with list generation, not
enumeration. By the time enumeration rolls around this should all have

been
sorted out).


It does depend on how we're storing the list. This will sort itself out
only if we expand the list into it's composite objects at list generation
time, so that:

aList = [1..4] -> converted to aList = [1,2,3,4] at list generation time
and
stored as 4 separate comoposite objects at generation

Alternatively, I thought we were looking to generate the list above as:

aList = [1..4] -> converted to aList = [Range(1..4)] so that the single
composite object Range would be stored

If this is the case then we have one action path at list generation and 2
separate action paths at list enumeration time, based on each type of
object.

The actual case will be something more like
public class InternalList<T>
{
public T this[int index]
{
get
{
//look up index, if its within a range, expand the range
//otherwise llook the index up directly.
}
}
}

It'll result in a more complicated list class (to support range splitting,
etc for inserts), but I can handle writing that in a support class. This
particular feature will probably require a extra library simply to allow
inter-class compatibilty.
Right. There are there goals I'm working towards:
1) allow ranges in lists
2) allow ranges to be placed in variables.
3) allow variables holding ranges to be loaded into lists
If we define Ranges as a type, we should be able to do this without much
trouble.


Ya, except I wanted to define Ranges as IEnumerables, which doesn't work. ;)
4) allow sub lists, arrays, etc to be added to lists.
5?) allow lists to be added to lists?
A question about adding arrays, lists, ranges - In the following:

Range = 1..5;
List = [Range];
Range = 6..10;

The contents of the List would be populated via the Ranges ICloneable
interface? So that List would not change, or does the List actually
contain
a reference to Range (in which case the contents of List would be [6..10]
after Range is changed)?


In that case its irrelevent.
Range = 6...10; creates a *new* object. You would just be replacing a
variables value, not the variable itself. IMHO, ranges should be immutable
and joins should result in an entirely new range being generated.
Maybe ICollection *is* better. Lists of lists could be constrained by

using
a syntax that explicitly marks an ICollection as a single object, not as
a
list to enumerate?


The ICollection does look like it might be a way to go. I think that's
more
of the direction I'm leaning - a single List object composed of other
single
objects (Range objects, string objects, list objects, chars, ints, etc.)

Nov 16 '05 #99

"Daniel O'Connell [C# MVP]" <onyxkirx@--NOSPAM--comcast.net> wrote in
message news:#Z**************@tk2msftngp13.phx.gbl...
Note that back to back in's is a big reason why I was pushing for isin in
another portion of the thread.


Yeah, when I saw how the comprehensions interact with other code, the 'in'
problem became clear.

Range = 1..5;
List = [Range];
Range = 6..10;


In that case its irrelevent.
Range = 6...10; creates a *new* object. You would just be replacing a
variables value, not the variable itself. IMHO, ranges should be immutable
and joins should result in an entirely new range being generated.


That may be the case, but if we're assigning the range 1..5 to a variable,
and later assign the range 6..10 to the same variable... I suppose my first
question is what happens to the original 1..5 range? I would expect that
(if it were an object) it would just be disposed of and the 6..10 range
replace it. The main question though was what does the variable List
contain after the instructions above? Is it [1..5], [6..10] or some other
value/values?
Nov 16 '05 #100

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: cody | last post by:
What about an enhancement of foreach loops which allows a syntax like that: foeach(int i in 1..10) { } // forward foeach(int i in 99..2) { } // backward foeach(char c in 'a'..'z') { } // chars...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.