This project is read-only.

Syntactic sugar for IEnumerable<T>

Topics: C# Language Design
Apr 5, 2014 at 8:32 PM
One of the things I find myself writing again and again is IEnumerable<T>. It's such a core part of the language that I think it should have a short form. After all, Nullable<T> is used a lot less frequently and has the T? short form.

Perhaps T^ or something?
Apr 6, 2014 at 1:12 AM
If doing this, why not provide a short syntax for dual IObservable<T>?
Apr 7, 2014 at 2:14 AM
Edited Apr 7, 2014 at 2:14 AM
I'd say because IObservable isn't part of the BCL and is used a lot less than IEnumerable.
Apr 7, 2014 at 4:18 AM
Edited Apr 7, 2014 at 4:18 AM
Actually IObservable is part of BCL, it's in mscorlib. (But the rest of Rx isn't.)

A possible solution of this would be to allow generic usings to create aliases, something like:
using seq<T> = System.Collections.Generic.IEnumerable<T>;

…

seq<int> numbers = Enumerable.Range(1, 100);
I'm using F#'s name (seq), but obviously everyone would be able to create their own aliases.
Apr 7, 2014 at 4:18 AM
An idea on syntax: Since [] is used for container type of stuff e.g. int[] is an array; how about []T as an equivalent ot IEnumerable<T> ie using the array reference as a prefix.
Apr 7, 2014 at 4:25 AM
Edited Apr 7, 2014 at 4:26 AM
@svick
using seq<T> = System.Collections.Generic.IEnumerable<T>;
It's a great idea. In fact it can be simplified to:
using seq = System.Collections.Generic.IEnumerable<>;
Apr 7, 2014 at 5:25 AM
using seq = System.Collections.Generic.IEnumerable<T>;
If this is going to be supported (which I think may be useful) then it should work for all classes, not just generic ones.

Perhaps there's other things that should be supported with this syntax as well?
Apr 7, 2014 at 11:28 AM
@mirhagk It already does, the syntax I showed (which is not my idea) is just an extension of syntax that worked since C# 1 (I think). The following works today:
using c = System.Console;
using seqInt = System.Collections.Generic.IEnumerable<int>;
Apr 7, 2014 at 11:32 AM
Edited Apr 7, 2014 at 3:59 PM
@bpschoch I think that syntax is weird and confusing. How would you differentiate between IEnumerable<T[]> and IEnumerable<T>[]? Using your syntax, both would be []T[]. Would you have to use parentheses or something like that to disambiguate?
Apr 7, 2014 at 1:35 PM
Edited Apr 7, 2014 at 1:35 PM
I agree with svick, it is more related to the lack of a proper equivalent of typdef or type aliasing (supporting generics), but using is too restricted as it is not an importable declaration and need to be copied everytime in the file where you need the alias. I would prefer a lot something that allows typedef
Apr 7, 2014 at 2:00 PM
I would prefer a lot something that allows typedef
More and more, I'm wondering if a pre-processing macro engine would be handy in C#. I'm finding a lot of things here that people could solve with a simple macro.
Apr 7, 2014 at 2:13 PM
mirhagk wrote:
I would prefer a lot something that allows typedef
More and more, I'm wondering if a pre-processing macro engine would be handy in C#. I'm finding a lot of things here that people could solve with a simple macro.
I would love to have an integrated meta/templating system into the language (statically verified/compiled, not the awful C/C++ preprocessor. There are plenty of languages that have such capabilities, with various degree of easiness/awfulness in their declaration style), but imho, type aliasing should be declared by a special statement, so that it can be verified, part of the "type system", clearly identified in the documentation... etc.
Apr 7, 2014 at 3:24 PM
What about typedefs that aren't aliases?

It can be useful to have an int that can't be converted to another int. It'd be nice to take a decimal type and say class money = decimal; or something like that and now a new class Money is created which is identical to decimal except it can't be automatically converted to decimal (maybe it can always be explictly converted). It's like the next level of type safety, checking use case and not just implementation.

With C#'s extensions methods this could potentially be very powerful and convenient.
Apr 7, 2014 at 6:40 PM
I have to say I didn't have an "Awesome!" moment when I first read about proposed C#6 features.
I think it's starting to go too much in the cryptic direction. Like: do we really need to be able to write
public double Length => Math.Sqrt(X * X + Y * Y);
instead of
public double Length { get { return Math.Sqrt(X * X + Y * Y); } }
Not sure :(

As much as I agree IEnumerable<T> is a type that is at the core of .net code, I certainly don't want to see hieroglyphic-like source code crippled with T^ or []T.

I can do with IEnumerable<T>, I could do with a nice shorthand, but please keep it clean, simple and readable.
Apr 7, 2014 at 8:42 PM
If there's one thing that I've learned from reading this forums is that everything can be solved by a macro system ;)

Please lets not reduce every discussion to that. A macro system is another topic.
Apr 7, 2014 at 10:14 PM
@Grokys,

How often do you really have to write IEnumerable<T> to justify adding a shorthand to the language?
Apr 7, 2014 at 10:15 PM
Generic type definition aliases. That would be cool.
Apr 8, 2014 at 4:09 AM
Edited Apr 8, 2014 at 7:18 PM
I like the syntax that svick suggested,
using seq = System.Collections.Generic.IEnumerable<>;
Although I'm not sure I understand why you're using the full notation that much. I use IEnumerables a lot, they're probably one of my favourite parts of the language at this point, but even so, it's pretty rare that I have to write the name out. I use var for variables, which if I'm not mistaken only leaves some outlier cases (say, casting if you're doing some weird reflection things, maybe) and method declarations. As for saving the small amount of time I spend declaring such methods (relative to the time I spend putting code into them, when I can use var freely), when I type "IE" in VS, IntelliSense jumps to IEnumerable. Not to pick too heavily at the details of this specific example, but speaking in real-world terms, typing "se" (of "seq") jumps to SerializableAttribute, which will fall before seq anyway. So that needs an extra keystroke.

Again, I don't want to focus too much on the specific example of shortening IEnumerable to seq, since I can imagine this being useful in other circumstances, but I'm just not convinced that this is worth, as jods pointed out, moving the whole language in an even less readable direction. Back to my original point of when I actually use IEnumerable<T> typed out, I feel like method declarations deserve to be as readable as we can make them anyway. So I feel like "var" does away with any deserving need for such a keyword.

But if we were sold on the idea, I can imagine generalizing the using directive using seq = System.Collections.IEnumerable; to use seq as a shortcut to the code string System.Collections.IEnumerable as compared to a shortcut to the class. That would let us say seq<T> and it could "expand" out to what we wanted. This seems clean enough, and I'm far from a language designer so I don't really know how using seq = System.Collections.IEnumerable; works or whether it could be adapted to work that way, but it seems like it would make sense from a syntax perspective.

As for the idea of a Nullable-esque T^ syntax, I think that would be far too significant a readability hit. I might get in trouble for saying this, but hear me out: when you make a type nullable, you're hardly changing the type. When I go from an int to an int?, I'm still dealing essentially with an int, there's just a little wrapper that makes me do a check and call a property before I can get at the actual value. The idea of a nullable type is pretty simple. But when I go from an int to an IEnumerable<int>, I'm dealing with a completely different set of tools. I can't really think about those two types in the same way, because I have to account for an entirely new dimension. There's a fundamental philosophical difference, I think, that isn't strictly true but shows how I think about the two types in an English form: a Nullable<int> is an int that can be null, and an IEnumerable<int> is an IEnumerable with int members. In the first case, we focus on the fact that it's an int, thus int? is appropriate. In the second case, we focus on the fact that it's an IEnumerable, thus having a small character after the "int" could easily be confusing.
The retort could be made that T^ looks pretty similar to T[] and nobody can hate on that syntax, but I just feel like using a single, new character would be too unusual to be worth the time we'd probably take getting used to it. I wish the syntax could be something that more reflected the idea of a collection, like T{} or something, of course that wouldn't work because it would be independently confusing, but you see my point.
Apr 8, 2014 at 1:45 PM
Having a way to typedef without modifying every file as with using would be good, but what would be even better is haskell's newtype.
On classes newtype can be somewhat simulated with inheritance, but the main use would be for sealed types like int or string, typically for statically-checked id types.