What should a "Stream" operator look like in C#?

Streams were one of the core concepts that I latched onto with Cw. It elevated enumerable lists as a first class problem domain. The fact that int* really was IEnumerable<int> melted away as an implementation detail and allowed me to begin to think in terms of lists ala (LISP/Scheme). This way of thinking seems to be extremely core to LINQ since the underpinnings are all based on IEnumerable<T>. This is why I feel very strongly that IEnumerable<T> deserves some special consideration by the language. If Nullable<T> can have its own operator where's the love for IEnumerable<T>?

 Now I'm by no means a compiler writer, but that didn’t stop me from pondering what kind of operator could be used for IEnumerable<T>. As I understand it, the core issue is picking an operator that will not break existing code. Cw used the * operator which would conflict with unsafe pointers in C#.

 Here's my first stab at it:

 int?                  Nullable<int>             C# 2.0
List<int>           List of ints                  C# 2.0
int[]                  Array of ints               C# 1.0
int{}                 IEnumerable<int>       C# 3.0 ?

It would seem that this could be parsed in the same way that an array declaration is but using {}. I'm sure there are huge holes in this that I have just missed. {} are typically used for code blocks so hopefully this difference is discernable by the compiler. This seems to make some sense in that at a later time you will use closures to perform operations on the stream ala foreach in C# and apply to all in Cw (more on that later). Conceptually it seems to pair well with arrays. [] are static lists where {} are dynamic lists or lazy lists.

Now on to the next Cw feature that I would love to see in some form in C# 3.0, Apply to all.

In Cw there was a shorthand for foreach that would "apply" a lamda to a stream:

Cw:

int* evenNumbers = EvenIntegers();
evenNumbers.{ Console.WriteLine(it); }

C# could be:

int{} evenNumbers = EvenIntegers();
evenNumbers.{ it => Console.WriteLine(it) };

This could generate method calls just like select/where do:

evenNumbers.ForEach( it => Console.WriteLine (it) );
or
evenNumbers.ApplyToAll( it => cw(it) );

ForEach is already a method on the List class in 2.0. Because of my prior experience with Cw I tend to prefer ApplyToAll but either name is equally good.

The ForEach/ApplyToAll could be provided by extension methods for IEnumberable and would be usable with LINQ query results.

(from p in Process.GetProcesses()
where p.WorkingSet > 4000000).{
            p => Console.WriteLine(p.MainModule.ModuleName)
}

Now, since I'm no expert in Cw or language design I'm sure there are all kinds of problems with these examples. I mostly want to point out how foundational "lists" seem to be in the future language features and as such should be given some special consideration by the language. Any time that I have to dip down into the implementation details the higher order concept gets lost.

4 Comments

  • You wouldn't get the first-class syntax that Cw gave you, but you could try your hand at implementing Apply() as an extension method to IEnumerable&lt;T&gt;. They may actually already provide such a thing.

  • Great suggestions. I'll definitly tak a look at Boo. I guess one of the coolest things about the direction Microsoft is taking is that everyone's getting interested in higher order languages again.

  • There's really no reason why you can't use any non-alphanumeric that is present on the keyboard for a stream operator. Obviously * and ? are now taken, but in theory, a ~ or / could be used since you're combining a type with a notation to indicate IEnumerable&lt;T&gt;.



    SO then, the only question is what non-alphanumeric, easy to type operator best represents a stream?



    In some ways the Pipe (|) character works, a slash could also work.

  • Yeah I toyed around with the various shift-# keys.



    int! evenNumbers;

    int@ evenNumbers;

    int# evenNumbers;

    int$ evenNumbers; // i kind of liked this one

    // since it could be $tream

    int% evenNumbers;

    int^ evenNumbers;

    int~ evenNumbers;

    int| evenNumbers;



    I do agree that with type inference that several of the scenarios melt away. But when defining iterators it's nice to describe the return value of the iterator as a stream.

Comments have been disabled for this content.