Choosing a syntax for list generating expressions [closed]
Question
C# has generator functions which have syntax like:
IEnumerable<int> GetNats(int max)
{
for (int i=0; i < max; ++i)
yield return i;
}
A feature I am interested in for my programming language (a simple object-oriented programming similar to Java, Scala, ActionScript, and C#) are generator expressions. These are essentially syntactic sugar for generator functions.
My current favorite candidate is the following syntax:
IEnumerable<int> nats =
witheach (i in range(0, 42))
yield i * 2;
The expression range(0, 42)
is a built-in generator function.
So my question is what syntax would you prefer to see for generator expressions, in a C#/Java/Scala/ActionScript type language, and why?
Some factors that may influence responses are that like Scala and ActionScript, types in my language are declared after the type. For example:
var myVar : SomeType = initialValue;
Also anonymous functions look like this:
var myFunc = function(int x) {
return x + 1;
}
Other than that the rest of my language syntax resembles Java or C#. My language has a foreach
statement which is very similar to C#.
Solution
There's also Python's approach--no special syntax for generators; the presence of a "yield" statement is all it takes. Any statements that takes a block could be used, though I'd expect to find only loops used in practice:
IEnumerable<int> nats =
for (i in range(0,42))
yield i*2
String phrase = "I want to buy some cheese.";
IEnumerable<int> substrs =
for (i in 0 .. phrase.length)
for (j in i .. phrase.length+1)
yield phrase[i..j]
Here I'm assuming the right endpoint isn't included in a range.
To be completely honest, when I saw this in Python, I had to wonder: "at what cost?"
OTHER TIPS
Check out what F# does. You can do
seq { seq-expr } // IEnumerable<T>, a.k.a. seq<T>
[ seq-expr ] // list<T>
[| seq-expr |] // array<T>
where seq-expr is a form that includes most language constructs along with 'yield'. So e.g. you can write
seq {
for i in 0..9 do
for j in someColl do
if i <> j then
yield i*j
}
The F# compiler translates this code into a state machine implementation of IEnumerable (like C# does for iterator blocks).
I like this syntax because it means e.g. you can write the exact same code you would write to "do imperative stuff now", e.g.
for i in 0..9 do
for j in someColl do
if i <> j then
printfn "%d" i*j
but wrap that code in seq{} and use 'yield' and the code becomes a lazy IEnumerable instead.
IEnumerable nats = 0...42 or for generators IEnumerable nats = yield 0...42