I would like to see features to make it easier to create data types with value semantics. This includes both classes (reference types) and structs. Currently there is a lot of unnecessary work between Equals, IEquatable, IClonable, GetHashCode, operator==,
etc, with most of the work simple stuff that could be automated. For example, by default, you probably want equality and assignment to be defined as equality and assignment of fields, so there should be a way to get that automatically, and declaratively handle
exceptions via attributes.
Switching from a class to a struct, or vice versa, is painful, for no reason.
I should not have to choose between class or struct in order to be able to choose between heap or stack allocation. Heap or stack allocation should be a decision of the user of a data type, not the implementation of the data type. That is a more extreme
and probably not possible without breaking c#, because we need syntax to indicate what is a reference, and what is a value, as in C++.
We need the notion of const-ness and const-correctness that can carry through, so we know what is immutable at a deep level. Basically, we need a more mathematically oriented language.
I would like to see a JIT compiler that can perform as well as Java's hot-spot compiler on high performance computing applications.
Actually the language D has very sophisticated (transitive) const semantics.
In many cases I think defining const-ness or immutability is a definition-site problem. If a type is inherently mutable, I'm not so sure it makes sense to force a new immutable version of it. Types should be designed to work as immutable or mutable and the
type-system should maybe help you track if you let an impure object flow through a method call (e.g. by forcing you to mark the return type as Impure<T>.
For example, if you let in an impure object in an otherwise pure method and you then compute some result based on it - but accessing properties of the object changes the object itself (let's just say it's an evil object), then this means that the method
is still pure in a way but if you then call the same method again with the same object, it will break - because technically it's not the same object anymore. So the method is no longer reflexively pure.