foreach enhancement

  • Thread starter Thread starter cody
  • Start date Start date
cody said:
OK, OK it is more than a set. So the order matters and duplicates should be
allowed, but depending on the context the compiler might choose to optimize
away the order or th duplicates.

The compiler should process the expression [0...100, 50..75] depending on
the context.

if an expression if (a in [0...100, 50..75]) the compiler must recognize
that the second range can be optimized away so it emits

if (a>=0 && a<=100) {}

So long as it doesn't affect the meaning, that's fine.
in a foreach loop like this:

foreach (int a in [0...100, 50..75])
{
Doit();
}

the compiler shall emit following code:

for (int i=0; i<=100; i++)
{
Doit();
}
for (int i=50; i<=75; i++)
{
Doit();
}

Note that the loop body will be duplicated, I have no better idea yet.

Well, if it doesn't need to emit C#, only IL, there are more
alternatives...
 
cody said:
And I still cannot understand why they did it this way. Why not simply
automatically break each case and if you really need a fallthrough you would
have to use the keyword continue.

switch (a)
{
case 0: continue;
case 1: continue;
case 2:
Foo();
}

Why they favoured the least used case and not the most common one?

I'm not sure there's actually a need for a continue, to be honest.
Personally I'd have gone with just

case 0, 1, 2:
Foo();

and possibly allow multiple cases for readability:

case 0, 1, 2:
case 3, 4, 5:
Foo();

Any code between the cases (including an empty statement, just a semi-
colon) would make it a different case, so:

case 0, 1, 2:
;
case 3, 4, 5:
Foo();

would do nothing for 0, 1 or 2.
 
cody said:
I don't consider them sets. ALthough every example we've used has been a
set, one could easily break the set rule that there are no
duplicates([0...100,50..75]), making them array or list
literals(depending
on implementation, does it return an Array or a IList<T>, for example). A
set would be possible via these literals but not guarenteed by it.

I consider it a set since the expression in the brackets in a set
resulting
from a union from the sets 0...100 and 50..75.
The compiler will optimize this to simply 0..100. I don't think that set
literals needs a IList interface.
IEnumerable and the operator "in" would be enough. I cannot think of more
functionality that is needed.
Every variable that supports the operators <,<=,>,>= can be used in this
sets.
I often have the case: if (DateTime.Today >= dateFrom && DateTime.Today <=
dateTo) it would be simpler if we could write:
if (DateTime.Today in[dateFrom ..dateTo]).
Its a research language based off C# that is the result of the merging of
two other research languages: Xen and Polyphonic C#.
http://research.microsoft.com/Comega/

I don't like every thing about them, but the cardinalities are
interesting.

Seems extremeley complicated and I don't think it will make its way into
regular C# someday.
Putting SQL, Datasets or XML directly into the language seems a very bad
idea to me.
The chords of polyfonic C# seem interesting and but I cannot say that I
understood it: Multiple methods with just one body this is a bit
confusing.

As I said, I don't like every idea, but the cardinalities and a few other
bits are good ones. The entire featureset will almost certainly *not* make
it, however I wouldn't be surprised if little bits here adn there show up
over the next decade.
What do you mean with array literal? In the set, internally no real
set/collection is created, the set literals is just a set of parameterized
rules.
It wouldn't be good if foreach (int i in 0..1000000) creates a huge array
internally.
I cannot imagine what real array literals should be good for.

Array literals are usually good because people don't like having to type new
type[] {1,3,5,6}, etc. I've heard quite a bit of fuss based entirely on not
likeing the existing inline array syntax, array literals gets around that.
If literal arrays can be considered immutable, the compiler could also do a
few optimizations, but thats more compliated than I want to get into.

Now, as far as foreach int i in 0...10000000) creating an array, I've been
thinking and I think I have a solution thats a little bit yours and a little
bit mine, namely generator expressions(for lack of a better term):

0...10 results in an expression of type IEnumerable
[expression] returns a list or array based off its content list, enumerating
enumerable values, so
[0...10] creates a list or array with the values 0-10, but 0...10 just
creates an iterator that generates the values 0-10

Its an interesting idea, however it leaves some problems. Discounting syntax
collisions, there are issues like with datetime above, without special
casing the datetime type, comparing between times would be *very* slow, as a
generator expression would have to generate a value for every tick. You
could use an increment clause but I'm not sure how to get that to work with
datetime without adding considerable complexity to the language(timespan
literals would help here, but they are rather rarely used things and
probably don't warrent addition).

The other option is to special case the in operator(or, as I'd rather do,
rename it to isin) as you suggested so that it doesn't emit the generator
expression, instead taking the values from it. This could be complicated to
do in situatinos like:

int lowerBound = 555;
int upperBound = 8137;
if (x isin [lowerBound...upperBound])

but I think it could be done.

For what its worth, isin would be an operator that takes a value x and
checks if it is in the list(or range) specified, returning a bool. It would
be able to stand alone, in other words
bool valid = 12 isin myNumberList;

such an operator would be required for functionality like this, IMHO.

The minimum requirements for ranges would probably have to be
T operator+(T,int) //Allow default increments, ie x...y where x and
y are T
T operator+(T,T) //Allow incrementing using T, ie x...y : z where
x, y, and z are T
bool operator==(T,T)
bool operator!=(T,T)
bool operator<(T,T)
bool operator>(T,T)
bool operator>=(T,T)
bool operator<=(T,T)
--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
What do you mean with array literal? In the set, internally no real
set/collection is created, the set literals is just a set of parameterized
rules.
It wouldn't be good if foreach (int i in 0..1000000) creates a huge array
internally.
I cannot imagine what real array literals should be good for.

Array literals are usually good because people don't like having to type new
type[] {1,3,5,6}, etc. I've heard quite a bit of fuss based entirely on not
likeing the existing inline array syntax, array literals gets around that.
If literal arrays can be considered immutable, the compiler could also do a
few optimizations, but thats more compliated than I want to get into.

Now, as far as foreach int i in 0...10000000) creating an array, I've been
thinking and I think I have a solution thats a little bit yours and a little
bit mine, namely generator expressions(for lack of a better term):


Why not just making {1,2,3} an array literal? It is just a shorthand for new
int[]{1,2,3}.
The type is taken from the element literals, the compiler must just ensure
that all variables in the array literal have the same type.

0..10 is a set literal (or maybe "enumerable ordered set generator literal"
:) ).

So we can write if (a in 0..10) and we don't need the brackets.
if we have multiple ranges we can use parentheses: if (a in (0..10, 30..40))
0...10 results in an expression of type IEnumerable
[expression] returns a list or array based off its content list, enumerating
enumerable values, so
[0...10] creates a list or array with the values 0-10, but 0...10 just
creates an iterator that generates the values 0-10

Its an interesting idea, however it leaves some problems. Discounting syntax
collisions, there are issues like with datetime above, without special
casing the datetime type, comparing between times would be *very* slow, as a
generator expression would have to generate a value for every tick.


We can simply that by default operator+(Type t, int n) is used for
increment.
If it is not present in the type we have to explicity state an increment
value.
This loop enumerates all days between today and the 31.12.2020 (we set the
increment to TimeSpan.TicksPerDay here)

foreach (DateTime dt in DateTime.Today..new DateTime(31,12,2020) :
TimeSpan.TicksPerDay ) {}
You could use an increment clause but I'm not sure how to get that to work with
datetime without adding considerable complexity to the language(timespan
literals would help here, but they are rather rarely used things and
probably don't warrent addition).

The other option is to special case the in operator(or, as I'd rather do,
rename it to isin) as you suggested so that it doesn't emit the generator
expression, instead taking the values from it. This could be complicated to
do in situatinos like:

int lowerBound = 555;
int upperBound = 8137;
if (x isin [lowerBound...upperBound])


What do you mean with that? Why do we need to rename "in" to "isin"?
For what its worth, isin would be an operator that takes a value x and
checks if it is in the list(or range) specified, returning a bool. It would
be able to stand alone, in other words
bool valid = 12 isin myNumberList;

This gives me the idea that it should be possible to store such a set
literal in a variable. And which already existing keyword wouldn't fit
better:

set mySet = (1..10, 30..100);

if (a in mySet) {}
The minimum requirements for ranges would probably have to be
T operator+(T,int) //Allow default increments, ie x...y where x and y are T
T operator+(T,T) //Allow incrementing using T, ie x...y : z where x, y, and z are T
bool operator==(T,T)
bool operator!=(T,T)
bool operator<(T,T)
bool operator>(T,T)
bool operator>=(T,T)
bool operator<=(T,T)

I think we do not need operator!=(T,T). operator+(T,int) is only required if
no increment literal is specified and operator+(T,X) is only required if an
increment literal of type X is specified.
 
And I still cannot understand why they did it this way. Why not simply
I'm not sure there's actually a need for a continue, to be honest.
Personally I'd have gone with just

case 0, 1, 2:
Foo();

and possibly allow multiple cases for readability:

case 0, 1, 2:
case 3, 4, 5:
Foo();

Any code between the cases (including an empty statement, just a semi-
colon) would make it a different case, so:

case 0, 1, 2:
;
case 3, 4, 5:
Foo();

would do nothing for 0, 1 or 2.


This is great! It acually allows all thinkable cases without the need of any
keyword!
You certainly are the next candidate for the turing award in my eyes .)
 
I actually looked it up on MS's website yesterday. The design team says
they forced a flow control statement after each case so that C++ programmers
wouldn't get confused by the lack of support for fall-through... Personally
I think anyone who has the brains to comprehend C++ code could probably
handle a slight change to the switch statement. LOL.

http://msdn.microsoft.com/vcsharp/team/language/ask/switch/default.aspx

Cheers,
Michael C.

cody said:
It's fine - so long as you don't have code in between. You can do:

case 0:
case 1:
Foo();
break;

but you can't do:

case 0:
Foo();
case 1:
Bar();
break;


And I still cannot understand why they did it this way. Why not simply
automatically break each case and if you really need a fallthrough you would
have to use the keyword continue.

switch (a)
{
case 0: continue;
case 1: continue;
case 2:
Foo();
}

Why they favoured the least used case and not the most common one?

--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
OK, OK it is more than a set. So the order matters and duplicates should be
allowed, but depending on the context the compiler might choose to optimize
away the order or th duplicates.

The compiler should process the expression [0...100, 50..75] depending on
the context.

if an expression if (a in [0...100, 50..75]) the compiler must recognize
that the second range can be optimized away so it emits

if (a>=0 && a<=100) {}

in a foreach loop like this:
foreach (int a in [0...100, 50..75])
{
Doit();
}

the compiler shall emit following code:

for (int i=0; i<=100; i++)
{
Doit();
}
for (int i=50; i<=75; i++)
{
Doit();
}

Note that the loop body will be duplicated, I have no better idea yet.

Hmmm... would that give the correct result? You're performing the operation
127 times. Should the compiler optimize away the 50..75 so that it only
runs 101 times? I'm thinking of a situation like this:

foreach (int a in [1...50, 50..100])
{
Doit();
}

This will give us 101 repititions because of the overlap at 50 using the
idea above; but the programmer may just be looking for 100 repititions in
this instance. Hmmm...

Cheers,
Michael C.
 
Any code between the cases (including an empty statement, just a semi-
colon) would make it a different case, so:

case 0, 1, 2:
;
case 3, 4, 5:
Foo();

would do nothing for 0, 1 or 2.

And if you absolutely need a fall through, just use the current C# scheme
for fall through cases - namely the goto statement:

case 0, 1, 2:
FooBar();
goto case 3; // goto case 4 or goto case 5 would be equivalent in this
instance
//- the compiler will pick the target at compile so
it really doesn't matter
case 3, 4, 5:
Foo();

-----
By the way, I 'asked the designers' why they didn't include a short-hand
case statement like we've been discussing, and here's the response:

"We left the case syntax the way it was in C because we didn't see a big
reason to change it. While your suggestion is slightly more convenient, we
decided the difference wasn't big enough to warrant a change in syntax.

Similarly, on case ranges, we didn't see enough utility there to warrant
adding extra syntax, though we might revisit this decision at some time in
the future."

Cheers,
Michael C.
 
Why not just making {1,2,3} an array literal? It is just a shorthand for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes with a
single contained semicolon.
[] has no stand alone meaning I can think of, its tied to identifiers and
attributes, but I think it would be far more feasible to write a compiler
that works that way. Its certainly not guarenteed, but I think the chances
are better.

Attributes may throw some nasty bits into field assignments, I fear. One
could also probably use
The type is taken from the element literals, the compiler must just ensure
that all variables in the array literal have the same type.

0..10 is a set literal (or maybe "enumerable ordered set generator
literal"
:) ).

This does generate a set, I'll agree. However I think it fits better as a
sequence(or ordered set) generator instead of as a set expression\literal.
Instead of returning a "set", it returns an enumerator.
So we can write if (a in 0..10) and we don't need the brackets.
if we have multiple ranges we can use parentheses: if (a in (0..10,
30..40))

I'd rather write:
if (a in 0...30 || a in 30...40)

instead of adding extra parantheses. I worry about the compiler architecture
that would require(parentheses have specific meanings already, this would
overload them, which i'm always hesitant to do).
We can simply that by default operator+(Type t, int n) is used for
increment.
If it is not present in the type we have to explicity state an increment
value.
This loop enumerates all days between today and the 31.12.2020 (we set the
increment to TimeSpan.TicksPerDay here)

foreach (DateTime dt in DateTime.Today..new DateTime(31,12,2020) :
TimeSpan.TicksPerDay ) {}

This works pretty well, I have to admit. I didn't think of the various
TimeSpan values, although operator+(DateTime,TimeSpan) would be ideal in
this situation as well.
You could use an increment clause but I'm not sure how to get that to
work with
datetime without adding considerable complexity to the language(timespan
literals would help here, but they are rather rarely used things and
probably don't warrent addition).

The other option is to special case the in operator(or, as I'd rather do,
rename it to isin) as you suggested so that it doesn't emit the generator
expression, instead taking the values from it. This could be complicated to
do in situatinos like:

int lowerBound = 555;
int upperBound = 8137;
if (x isin [lowerBound...upperBound])


What do you mean with that? Why do we need to rename "in" to "isin"?

Syntactic clarity, basically. in has a meaning.
Consider
foreach (x in y)
and
if (x in y)

in that case, in has two *very* different meanings in two *very* similar
situations. Using isin would skip that by creating a new keyword without
existing meaning in similar circumstances. things like default(which I don't
agree with, defaultof makes more sense) work because the usages are
different(default(x) and switch { default: break; } are very different.

I'm not advocating using isin in foreach, just as general containment
keyword.
This gives me the idea that it should be possible to store such a set
literal in a variable. And which already existing keyword wouldn't fit
better:

That requires definition of a new type, ISet, which we don't have yet.
However I wouldn't be terribly against the concept. Although a readonly
array would do the job fine. This however does lead back into the needs of
the general collections in the BCL.

Now, if you would *stop* insisting that it is a set literal and instead
consider it array or list literals, you don't need any additions,
IList\ICollection and Array do the job sufficently.
set mySet = (1..10, 30..100);

Again, parentheses wouldn't work here due to their existing meaning(grouping
expressions). Thats why I've proposed brackets a few times.

In this situation, however, you are forced to decide on ordered sets and on
lists. With lists you end up with two different sets of values added,
whereas if you are using sets you would use unions, etc instead. That would
clear up the need for parentheses here for a set

1...10 union 30...100 (can't think of a symbol for unions off hand).

Generator expressions could use set syntax while list literals could use
commas to seperate several list generators.

This does lead to the need of a set type, one that is defined by an equation
instead of by a literal and members are generated via an iterator as needed.

Sounds a bit functional, doesn't it.
if (a in mySet) {}


I think we do not need operator!=(T,T). operator+(T,int) is only required
if
no increment literal is specified and operator+(T,X) is only required if
an
increment literal of type X is specified.

I agree on addition, standard adding rules works pretty well.

You have a point on !=, can't think of a use. However != is supposed to be
implemented when == is, so its required by default.
--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
Jon said:
Well, in beta...


What exactly do generics have to do with this? I can't see any
connection, myself.

http://www.ondotnet.com/pub/a/dotnet/2002/12/02/generics.html

You create a new set by specifying actual types to replace the formal
parameter:


using GCollections;
ISet<string> MyStringSet = new HashSet<string>();



This creates a set of strings, called MyStringSet. We can use this to write
a new variation on the classic first program. Note that we can use the C#
foreach construct even with a generic class defined in user code.


using GCollections;

class M {
public static void Main() {
ISet<string> MyStringSet = new HashSet<string>();
MyStringSet.Add("hello");
MyStringSet.Add("world");
foreach (string i in MyStringSet)
System.Console.WriteLine(i);
}
}
 
Sam Palmisamoo said:
http://www.ondotnet.com/pub/a/dotnet/2002/12/02/generics.html

You create a new set by specifying actual types to replace the formal
parameter:


using GCollections;
ISet<string> MyStringSet = new HashSet<string>();



This creates a set of strings, called MyStringSet. We can use this to write
a new variation on the classic first program. Note that we can use the C#
foreach construct even with a generic class defined in user code.

using GCollections;

class M {
public static void Main() {
ISet<string> MyStringSet = new HashSet<string>();
MyStringSet.Add("hello");
MyStringSet.Add("world");
foreach (string i in MyStringSet)
System.Console.WriteLine(i);
}
}

That's hardly addressing the original question though. In particular,
there's no need for generics for any of that - you can use a Hashtable
today for set semantics, or an ArrayList for a sequence. Alternatively,
just create an array:

foreach (string i in new string[]{"hello", "world"})
{
....
}

My point is that generics doesn't actually do much for what cody's
after, which is a far more compact way of expressing sets and sequences
for use in foreach and if statements.
 
Michael C said:
foreach (int a in [0...100, 50..75])
{
Doit();
}

the compiler shall emit following code:

for (int i=0; i<=100; i++)
{
Doit();
}
for (int i=50; i<=75; i++)
{
Doit();
}

Note that the loop body will be duplicated, I have no better idea yet.

Hmmm... would that give the correct result? You're performing the operation
127 times. Should the compiler optimize away the 50..75 so that it only
runs 101 times? I'm thinking of a situation like this:

foreach (int a in [1...50, 50..100])
{
Doit();
}

This will give us 101 repititions because of the overlap at 50 using the
idea above; but the programmer may just be looking for 100 repititions in
this instance. Hmmm...

In that case the programmer is being foolish, in my view. If you ask
for two sequences which happen to share an endpoint, you should expect
to see the endpoint twice, IMO. I would be very confused if it *didn't*
give 101 iterations, personally.
 
I actually looked it up on MS's website yesterday. The design team says
they forced a flow control statement after each case so that C++ programmers
wouldn't get confused by the lack of support for fall-through... Personally
I think anyone who has the brains to comprehend C++ code could probably
handle a slight change to the switch statement. LOL.

http://msdn.microsoft.com/vcsharp/team/language/ask/switch/default.aspx


Hm very strange they must think programmers are stupid. If they let their
decisions lead by such thoughts, I'm surprised that they made it into
createing a new language at all.

Maybe it is the same with the private keyword. It is completly useless
because everything is private by default, but maybe they thought: "What if
C++ programmer will miss it?"
 
Why not just making {1,2,3} an array literal? It is just a shorthand for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes with a
single contained semicolon.


We could simply say if a {} expression contains at least one semicolon or is
emtpy
then it is a codeblock, otherwise it is an array literal. An array literal
cannot be empty
(the compiler would not be able to determin the type) and is not allowed to
contain semicolons.

{ } // block
{ Foo(); } // block
{1} // array literal
{1,2} // array literal

Alternatively we can use the following facts to distinguish between them:
Array literals can never be used where also a block would be valid and vice
versa.

int[] a = {1,2}; // here only an array literal can be valid

Foo()
{
int a=0;
{ int a=0; } // here only a block can be valid
}

Foo({1,2}); // here it can be only an array literal

{1,2}.IndexOf(a); // methods cannot be called on a block which means it can
only be an array literal
[] has no stand alone meaning I can think of, its tied to identifiers and
attributes, but I think it would be far more feasible to write a compiler
that works that way. Its certainly not guarenteed, but I think the chances
are better.

Attributes may throw some nasty bits into field assignments, I fear. One
could also probably use
<0...10> as well, but again I would have to experiment with a compiler to
know for sure.


<0..10> Is a bad idea because it can have lots of meanings and is used in
generics too.
Additionally it doesn't look any beautiful :(

the braces are imho the best choice since they are already used to
initialize arrays
in c/c++ I can write: int[] a ={1,2}; so why not in c#?
Syntactic clarity, basically. in has a meaning.
Consider
foreach (x in y)
and
if (x in y)

in that case, in has two *very* different meanings in two *very* similar
situations. Using isin would skip that by creating a new keyword without
existing meaning in similar circumstances. things like default(which I don't
agree with, defaultof makes more sense) work because the usages are
different(default(x) and switch { default: break; } are very different.

I'm not advocating using isin in foreach, just as general containment
keyword.


I do not agree that we have to invent a new keyword.
A programmer will be able to distinguish between a foreach loop and a "if (a
in [1,2])" expression.
Most programming languages use one and the same keyword for different
purposes which is not bad imho.
That requires definition of a new type, ISet, which we don't have yet.
However I wouldn't be terribly against the concept. Although a readonly
array would do the job fine. This however does lead back into the needs of
the general collections in the BCL.

Now, if you would *stop* insisting that it is a set literal and instead
consider it array or list literals, you don't need any additions,
IList\ICollection and Array do the job sufficently.


We can neither use IList nor Array since the set is readonly.
I think it doesn not need to be an object at all.
If we write foreach (int a in 0..10) the compiler just generates for (int
a=0;a<=10;i++) nothing more.
That would mean I cannot store the set into a variable but who cares.
Again, parentheses wouldn't work here due to their existing meaning(grouping
expressions). Thats why I've proposed brackets a few times.


Agreed the brackets [] seems to be the best choice for set literals..
 
cody said:
Why not just making {1,2,3} an array literal? It is just a shorthand
for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically
possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes
with a
single contained semicolon.


We could simply say if a {} expression contains at least one semicolon or
is
emtpy
then it is a codeblock, otherwise it is an array literal. An array literal
cannot be empty
(the compiler would not be able to determin the type) and is not allowed
to
contain semicolons.

That shows that you have never written a compiler. Its not easy to determine
if a semicolon is contained within any given block as its handled by a
parser.
{ } // block
{ Foo(); } // block
{1} // array literal
{1,2} // array literal

what about
{
delegate(int x)
{
return 50;
}
}
?
Alternatively we can use the following facts to distinguish between them:
Array literals can never be used where also a block would be valid and
vice
versa.

int[] a = {1,2}; // here only an array literal can be valid

Foo()
{
int a=0;
{ int a=0; } // here only a block can be valid
}

Foo({1,2}); // here it can be only an array literal

{1,2}.IndexOf(a); // methods cannot be called on a block which means it
can
only be an array literal
[] has no stand alone meaning I can think of, its tied to identifiers and
attributes, but I think it would be far more feasible to write a compiler
that works that way. Its certainly not guarenteed, but I think the
chances
are better.

Attributes may throw some nasty bits into field assignments, I fear. One
could also probably use
<0...10> as well, but again I would have to experiment with a compiler to
know for sure.


<0..10> Is a bad idea because it can have lots of meanings and is used in
generics too.
Additionally it doesn't look any beautiful :(

the braces are imho the best choice since they are already used to
initialize arrays
in c/c++ I can write: int[] a ={1,2}; so why not in c#?

Because I think its too ambigious. C++ isn't the easiest langauge to write a
parser for, I imagine this is one of the major issues. I personally think
that using {'s is asking for extra compiler complexity without any real
benifits
Syntactic clarity, basically. in has a meaning.
Consider
foreach (x in y)
and
if (x in y)
I do not agree that we have to invent a new keyword.
A programmer will be able to distinguish between a foreach loop and a "if
(a
in [1,2])" expression.
Most programming languages use one and the same keyword for different
purposes which is not bad imho.

A programmer *can*, but I think its...silly to force them to. The language
doesn't force that in any other situation.
Also, you *could* end up with
foreach (X x in GetIterator(x in [x1...x2]))
{
}

It'd be far easier to screw up the two meanings of in in this case, it just
isn't clean enough, IMHO.

I also think its rather short sighted to overload a keyword simply because
*other* langauges do it. One would ideally want to design the langauge so
its *right*, not nessecerily like others. I think overloading keywords for
the sake of overloading keywords is just not the right approach. A
minimization of keywords is a good idea, but I don't agree with the overkill
involved.
We can neither use IList nor Array since the set is readonly.
Making the set read only is not definate. Nothing says it has to be.
Readonly is a seperate issue, it allows set interning, but isn't nessecery.
I think it doesn not need to be an object at all.
If we write foreach (int a in 0..10) the compiler just generates for (int
a=0;a<=10;i++) nothing more.
That would mean I cannot store the set into a variable but who cares.

In that case it doesn't, however if ranges are assignable for *other*
reasons, then it matters.

Again, parentheses wouldn't work here due to their existing meaning(grouping
expressions). Thats why I've proposed brackets a few times.


Agreed the brackets [] seems to be the best choice for set literals..

I agree, although I still think sets are not a nessecity. I'd rather see
list literals using [] instead of set literals.
--
cody

[Freeware, Games and Humor]
www.deutronium.de.vu || www.deutronium.tk
 
Daniel O'Connell said:
cody said:
Why not just making {1,2,3} an array literal? It is just a shorthand
for
new
int[]{1,2,3}.

Primarily because I'm concerned that {1,2,3} isn't syntactically
possible.
{ <expression> } is legal syntax in C#(creates a block), how do you tell the
difference in that and {<listexpression>} ? Considering 10...30 as a
generator expression, { 10...30; } is ambigious outside of the ;, and I
don't particually care for distinguishing two very different syntaxes
with a
single contained semicolon.


We could simply say if a {} expression contains at least one semicolon or
is
emtpy
then it is a codeblock, otherwise it is an array literal. An array
literal
cannot be empty
(the compiler would not be able to determin the type) and is not allowed
to
contain semicolons.

That shows that you have never written a compiler. Its not easy to
determine if a semicolon is contained within any given block as its
handled by a parser.
{ } // block
{ Foo(); } // block
{1} // array literal
{1,2} // array literal

what about
{
delegate(int x)
{
return 50;
}
}
?

Thinking about it a bit more, it may be possible using something
like(hopefullyu I got the syntax right, don't have a file open on hand)
array_literal_expression : OPEN_BRACKET expression_list CLOSE_BRACKET
;
expression_list
: expression
| expression_list expression
;

its worth looking into, I'll probably delve into mono again in a day or two
and see what can be done.
 
Hmmm... would that give the correct result? You're performing the operation
127 times. Should the compiler optimize away the 50..75 so that it only
runs 101 times? I'm thinking of a situation like this:

foreach (int a in [1...50, 50..100])
{
Doit();
}

This will give us 101 repititions because of the overlap at 50 using the
idea above; but the programmer may just be looking for 100 repititions in
this instance. Hmmm...

In that case the programmer is being foolish, in my view. If you ask
for two sequences which happen to share an endpoint, you should expect
to see the endpoint twice, IMO. I would be very confused if it *didn't*
give 101 iterations, personally.

So going back to the 'set' analogy, [1..50, 50..100] isn't a union of sets,
but rather shorthand for two separate sets?

Thanks,
Michael C
 
Michael C said:
Hmmm... would that give the correct result? You're performing the operation
127 times. Should the compiler optimize away the 50..75 so that it
only
runs 101 times? I'm thinking of a situation like this:

foreach (int a in [1...50, 50..100])
{
Doit();
}

This will give us 101 repititions because of the overlap at 50 using
the
idea above; but the programmer may just be looking for 100 repititions in
this instance. Hmmm...

In that case the programmer is being foolish, in my view. If you ask
for two sequences which happen to share an endpoint, you should expect
to see the endpoint twice, IMO. I would be very confused if it *didn't*
give 101 iterations, personally.

So going back to the 'set' analogy, [1..50, 50..100] isn't a union of
sets,
but rather shorthand for two separate sets?

IMHO, its a set\list\array(thats being argued) generated from a pair of
sequence generator expressions. Its one item constructed out of two seperate
sequences, in other words. Consider each 1...n expression to be a generator,
short hand for a list of values between each. When wrapped in the [] syntax,
it would be the same as calling Add on the appropriate structure for each
value. For a *set*, you end up with 100 entries as the set can only contain
unique values(or you end up with an exception, thats a matter of
implementation), if [] syntax generates a list or an array, then you end up
with 101 entries, with 50 entered twice.

While I could see Jon being confused if that syntax generated a *list* with
a single instance of 50, I would be too. However, I'm sure he'd agree that
if it is two generators as the source for a *set* 50 would logically only
exist once. Odd as it is, the answer this question depends entirely on what
the actual semantics of the feature actually are.

Now, a set itself is probably not correct here as a set has no inherent
order and simply doesn't solve the problem. I think the syntax should
generate a list, and hence Jon and cody's examples are correct in their
behaviour.

This particular situation is something I would probably consider for a
warning. Code structured like that is strange and I would suspect rare
enough to consider "potentially erroneous". The compiler should issue a
warning like "List on line XXX generates duplicate values", which I think is
sufficent.
 
Jon Skeet [C# MVP] wrote:

My point is that generics doesn't actually do much for what cody's
after, which is a far more compact way of expressing sets and sequences
for use in foreach and if statements.

This person seems to understand.


http://weblogs.asp.net/fmarguerie/archive/2003/11/10/36710.aspx


He talks about enums and sets, then goes on to mention how c# doesn't have
generics yet, which would be the more compact way to do what the OP wants.

He writes:

Just as a pretext, I tried to reproduce this behaviour in C#. We don't have
templates (oops, generics!) yet, so I went with code generation with
CodeSmith. The result is certainly not performant at all, but is an
interesting use of operator overloading and code generation.
Note that the result here is merely a simple collection and so very
different from Delphi's implementation of the concept!
I added however an implementation that works with flags enums (see the Flags
attribute) which allow enumerations to be treated as bit fields. This is
closer to what Delphi does and certainly more performant.
 
Michael C said:
In that case the programmer is being foolish, in my view. If you ask
for two sequences which happen to share an endpoint, you should expect
to see the endpoint twice, IMO. I would be very confused if it *didn't*
give 101 iterations, personally.

So going back to the 'set' analogy, [1..50, 50..100] isn't a union of sets,
but rather shorthand for two separate sets?

When used in a foreach statement, it shouldn't be regarded as a set at
all - it should be regarded as a sequence, IMO.
 
Back
Top