C# thrives on being a multi-paradigm language. One of those paradigms is the imperative one, which is useful in some scenarios depending on personal preferences.
However it seems that there are some discrepencies between general feature availability in imperative and functional paradigms in C#.
One of such features is anonymous types. While it is easily possible to create a list of anonymous instances via functional paradigm (LINQ monad), it's not that easy to create same list in an imperative way.
Please find comparative examples below.
Functional:
private void FunctionalSample()
{
var list = Enumerable
.Range(0, 10)
.Select(i => new {Id = i, Name = "Specimen #" + i})
.ToList();
// Further manipulations.
}
Imperative:
private void ImperativeSample()
{
// Is there any other way?
var list = (new[] {new {Id = -1, Name = "[ignored]"}}).Skip(1).ToList();
for (var i = 0; i < 10; i++)
{
list.Add(new { Id = i, Name = "Specimen #" + i });
}
// Further manipulations.
}
This is obviously a pain point:
http://stackoverflow.com/questions/15749445/is-there-a-trick-in-creating-a-generic-list-of-anonymous-type
http://stackoverflow.com/questions/612689/a-generic-list-of-anonymous-class
The issue could be looked at in a bigger picture, e.g. class-level local inference of anonymous types (limited to private members, e.g. would allow returning an explicitly anonymous type (as opposed to object) from a type's private methods.
Suggested syntax could be something like:
private void NewSyntaxSample()
{
// Using a suggested keyword, type inferred from first usage below.
var list = new List<anonymous>();
for (var i = 0; i < 10; i++)
{
list.Add(new { Id = i, Name = "Specimen #" + i });
}
// Further manipulations.
}
Taking constructs added to the language in order to facilitate the functional aspects and forcing them into the imperative syntax doesn't make a lot of sense. There was an imperative solution to that problem long before anonymous types and type inference were added to the language. I think allowing for type inference to be driven by a potential eventual usage is opening an error-prone and confusing can of worms.
Agree with @HaloFour
Moreover: if functional way is more suitable, why you dont want to use it?
@HaloFour I don't insist on class-level inference, since it will be covered by language-level tuples - updated the post accordingly. Method-scoped anonymous type inference is already present though, it's just not consistent.
@dimaaan I never said that. I use different approaches based on the problem at hand. Cramming everything into LINQ is not always a good idea.
@dsaf look at following code
``` c#
private void NewSyntaxSample()
{
var r = new Random();
// Using a suggested keyword, type inferred from first usage below.
var list = new List
if(r.Next() % 2 == 0)
{
list.Add(new { Id = 1, Name = "Specimen #1" });
}
else {
list.Add(new { Id2 = 1, Name2 = "Specimen #2" });
}
// How do i access list member??
}
```
@dimaaan This is not how anonymous types generally work.
Consider the following example demonstrating your code rewritten using today's C#:
private void DoesntWorkAlready()
{
var r = new Random();
var list = Enumerable
.Range(0, 10)
.Select(i => new { Id = i, Name = "Specimen #" + i });
if (r.Next()%2 == 0)
list = list.Concat(new[] {new {Id = 100, Name = "AAA"}});
else
list = list.Concat(new[] { new { Id2 = 100, Name = "AAA" } });
// The line above will give you an error ^.
}
@dsaf
Method-scoped anonymous type inference is already present though, it's just not consistent.
Only based on the immediate usage. Given assignments are right-to-left the compiler already knows the exact type at the point of inference. This would require that the compiler not know the type until some indeterminate point.
Consider the following example demonstrating your code rewritten using today's C#:
_Only_ because the compiler already knows the type based on the assignment to list. Without that the compiler can't determine the type of list until it hits one of the assignments. Clearly that would have to be a compiler error, but now it's far from the actual cause, and you cannot possible look at the declaration of list and know what it is upon instantiation.
@HaloFour I am inclined to agree now, after some consideration. Can you think of any other alternative?
How about this:
var list = (new[] {new {Id = -1, Name = "[ignored]"}}).Skip(1).ToList();
vs
var list = new List<new {int Id, string Name}>(); // Anonymous, yet specified immediately.
It does look more and more like tuples though...
var list = new List< new {int Id, string Name}>();
Looks better now
@dimaaan That might work in Java due to type-erasure, but it suffers from the same exact problem at above in C# because the type of the generic type parameter isn't known and it must be known in order to complete the type and instantiate it. There is no System.Collections.Generic.List in the BCL.
@HaloFour Well, a language-level list support is being considered, that might help, though it would push this change implementation into C# 8.
Technically if you care about the potential allocations, you could write a helper
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static List<T> CreateListForAnonymous<T>(T dummy)
{
return new List<T>();
}
...
// then later when you use it.
var mylist = CreateListForAnonymous(false ? new {Id = 0, Name = ""} : null)
This is awful but at least then you're not instantiating the object. The compiler will simplify that method invocation as CreateListForAnonymous(null), and the jitter will inline it to the same as a direct call to new List<anonymous object>.
@dsaf IIRC that's just C# syntax specifically related to creating System.Collections.Generic.List from literal expressions. The limitations/behaviors wouldn't change, just some of the syntax.
@HaloFour true, but surely compiler can emit System.Collections.Generic.List<AnonymousClass123>?
Actually I think we are discussing different versions of @dimaaan's comment. I am not talking about new List()
@mburbea I am more worried about readability than performance, but yes that is a clever trick. It would be nice to avoid having to create new adhoc classes or using library-level tuples when writing local imperative code.
@dsaf That it can, when it knows what AnonymousClass123 is. Which brings us back to the first problem. :grinning:
It amazes me how much unreadable mess developers are willing to write in order to avoid even the slightest boilerplate. A nested tuple/class is infinitely more readable. Messy tricks of inference should be avoided wherever possible. If the intention cannot _immediately_ be understood it already costs more than being explicit. Any given piece of code will be read by a human significantly more often than it will be written, it is that first cost that should be minimized as much as possible.
@dsaf @HaloFour Guys, thats my fault. First version of my last comment was new List(), because github eat some text.
But IMHO LINQ is more readable than var list = new List< new {int Id, string Name} >();.
That request may have some academic value, but useless in real world.
I mean this feature increase complexity of language, but gives no profit
@dsaf I view the proposed C# 7 tuples as pretty much a replacement for anonymous types (I doubt a language designed from scratch would have both). Is there any reason why you couldn't use a tuple instead of an anonymous type here?
You mention that tuples are "library-level". Is there anything specific about them that makes them unsuitable at a method level?
if c#7 tuples are struct type, there are the usual pitfalls of structs to consider and thus they can have different performance considerations. Putting a struct of multiple object references, on the stack and copying it when pulling it out can have disastrous considerations, when a class would not have those issues.
@mburbea Yes, they are structs (see #347). I don't think what you're describing would be a common case. And for uncommon cases, I think it's okay to make you use a named type to achieve better performance.
@svick When I said "library-level" I meant today's Tuple<int, string>, when I said "language-level" I meant tomorrow's (int, string). Yes, I could use language-level tuples, but @mburbea is correct about performance and also there is a possibility that anonymous types will allow subtyping and interfacing thus becoming a more flexible and powerful option.
@dimaaan, I'm the first in favor of a functional style, but I'm agree with @dsaf about consistency (of two styles) and the need of being able to handle anonymous type with easy also when using imperative paradigm.
Sometimes right in favor of defining a method that adheres to functional paradigm from the outside, you may need more imperative flexibility from the inside (e.g. to handle mutation).
For example even a F#聽(multiparadigm but mainly functional) implements List.map using mutation to favor performance.
About syntax, even if I'd like to see new keywords only when absolutely necessary, I think that what suggested at the end of first post is readable and autoexplicative.
I think this "problem" can be solved by making type inference a bit "better". Compiler could see that you are creating a generic List object without specifying generic type and then see that you are using method of that object that consumes object of generic parameter type. It may sound complicated, but it's actually easy to understand. Actually F# has this feature already. Here is example that use named type, because F# doesn't have anonymous one:
open System.Collections.Generic
type NamedType = {
Id : int;
Name : string;
}
let testFunc() =
let list = List<_>()
for i in 0..9 do
list.Add({Id = i; Name = "Specimen #" + i.ToString()})
list.Add({Id = 100; Name = "End"})
//list.Add("just string"); //error if uncomment "This expression was expected to have type NamedType but here has type string
list
let listOfNamedTypes = testFunc()
I am using wildcard character (_) when creating List object. Then the compiler see that you use Add(T item) and infer that T is of type NamedType. If you tried to do other Add call with different type then you'll have type inference compiler error.
C# could do the same, but probably with * character as wildcard (that was the proposition in pattern matching feature).
Personally I think it would be nice, because it's not only for using anonymous types but for also for other named types. So I could just avoid unnecessary typing that doesn't add much value to comprehend program logic and flow.
But I guess this feature doesn't add much value for given costs of development, so I don't have much hopes for this ;)
@mpawelski C# could definitely use a better type inference and lesser "ceremony" (although I am not a fan of F#'s total global inference) - a number of similar requests had already been raised, including #2319, #1470, #1419, #2397, #20.
@mpawelski,
this makes sense. C# actual type inference capability is too limited, maybe that it will never get F# type level type inference, but I think a lot can be enhanced.
"Better" is very relative. I'm personally not a fan of having type inference have to read through the remainder of the method to try to determine how the type will be used. It's more confusing to anyone who has to read and maintain that code.
@HaloFour Yes, it's very relative and subjective. Just like whole usage of implicit typing. Some people still don't like that we have something like var in C# (but I guess the number is declining ;)) but for many it's one of the best features introduced to C#. For me it's great that unnecessary type information don't bloat the code that you are reading and you can focus on understanding the flow and logic of code. In my experience when you have good naming of variables and methods then type information is unnecessary and just obscures the code. And if you want to know the type just hover the mouse over the var and IDE will tell you.
My proposition is just another "enhancement" to type inference that I think might be usefull and even improve readability of code.
When you say that
It's more confusing to anyone who has to read and maintain that code.
it reminds me that it's exactly what people were talking then var keyword was introduced to C#.
@mpawelski
It is all relative. I'm not opposed to some type inference. I like var (and have always liked var) and make frequent use of it. I'd like to see it expanded to delegates and fields as well. But I also like how simple the rules are.
Deferring the type inference based on usage complicates those rules and makes the developer have to keep additional things in mind when reading the code. It also introduces edge cases which would need solid rules, e.g.:
public class Baz { }
public class Foo<T1> {
public void Bar<T2>(T2 value) where T2 : T1 { }
}
...
var foo = new Foo<*>();
foo.Bar(new Baz()); // legal? If so, what is T1 or T2?
It's all a scale. The sweet spot is somewhere in the middle and different for everybody.
@HaloFour
foo.Bar(new Baz()); // legal? If so, what is T1 or T2?
- Legal, and this specific situation can be resolved by either using explicit syntax or defining rules of inference. Edge cases do not make the feature less useful for the majority of more common scenarios.
T1could be the "best common type" as mentioned in #1419; if the inferred type is not as expected (e.g. the immediate parent type), user can specify expected type explicitly (e.g. parent type higher in the inheritance chain).T2isBaz.
@HaloFour
I'm personally not a fan of having type inference have to read through the remainder of the method to try to determine how the type will be used. It's more confusing to anyone who has to read and maintain that code.
- Methods should not be so big that reading them becomes a problem.
- In 2015 it's an IDE problem really, it should show you the inferred type in a tooltip / badge.
- How do you feel about method-scoped types?
You probably want tuples (#347).
@gafter I guess, as long as fields/properties can definitely be named. Does it mean anonymous types will be "outdated"?
They "can't". They still have usages.
Tuples won't have names outside of the source code. So, they won't be bindable. With enough work, the names could be put in expression trees for queriables, but does it worth the work, given that anonymous types already exist?
@paulomorgado for relevenat discussion see #6877. Since tuples are value types and going to be more common to be used with LINQ I think it does.
@alrz, I'm aware of that discussion. But, while you think we can get rid of anonymous types, I think we can't.
If they get interfaces #13 they might become more interesting. But it feels like tuples will be preferable where possible. Of course you cannot get rid of anonymous types, they will just be rarely seen just like delegate, extern and goto.
@dsaf I think so. Unless you really want a reference type; assuming that expression trees support tuples, I can't think of any other use cases that you might prefer anonymous types over tuples.
We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages.