ML, Haskell, Scala, F #, SML, and other languages can easily determine the type from equivalent expressions in their own language, mainly because they were designed with the type in mind from the start. C # was not, its output type was used as a post-hoc solution to the problem of access to anonymous types.
I assume that the true Hindley-Milner type inference has never been implemented for C # because it is difficult to infer types in the language, so it depends on classes and inheritance. Let's say I have the following classes:
class Base { public void Print() { ... } } class Derived1 : Base { } class Derived2 : Base { }
And now I have this method:
var create() { return new Derived1(); }
What type of return is here? Is it Derived1 , or should it be Base ? If so, should it be object ?
Ok, now let's say that I have this method:
void doStuff(var someBase) { someBase.Print(); } void Main() { doStuff(new Derived1()); doStuff(new Derived2());
The first call to doStuff(new Derived1()) supposedly leads to doStuff type doStuff(Derived1 someBase) . Suppose now that we derive a specific type instead of a generic type T
What about the second call, doStuff(new Derived1()) ? Is this a type error, or are we generalizing instead of doStuff<T>(T somebase) where T : Base ? What to do if we made the same call in a separate assembly without links - the type inference algorithm would have no idea whether to use a narrow type or a more genenarlized type. Thus, we get two signatures of different types based on whether the method calls from within the assembly or from another assembly.
You cannot generalize wider types based on the use of a function. Usually you need to rely on one particular type as soon as you know which particular type passes. Thus, in the above code example, if you have not explicitly switched to the base type, doStuff limited to accept Derived1 types, and the second call is a type error.
Now the trick here settles on the type. What is going on here:
class Whatever { void Foo() { DoStuff(new Derived1()); } void Bar() { DoStuff(new Derived2()); } void DoStuff(var x) { ... } }
What type of doStuff ? In this regard, we know, based on the foregoing, that one of the Foo or Bar methods contains a type error, but can you determine who has the error?
Cannot resolve type without changing C # semantics. In C #, the method declaration order does not affect compilation (or at least should not;)). Instead, you can say that the method declared first (in this case, the Foo method) determines the type, so Bar has an error.
This works, but also changes the semantics of C #: changes in the method order will change the compiled type of the method.
But let's say we went further:
// Whatever.cs class Whatever { public void DoStuff(var x); } // Foo.cs class Foo { public Foo() { new Whatever().DoStuff(new Derived1()); } } // Bar.cs class Bar { public Bar() { new Whatever().DoStuff(new Derived2()); } }
Now methods are called from different files. Which type? It is impossible to solve without imposing some rules on the compilation order: if Foo.cs compiles before Bar.cs, the type is determined by Foo.cs.
Although we can enforce such rules in C # to do work with the type of inference, it would radically change the semantics of the language.
Unlike ML, Haskell, F # and SML, output type inference is so good that they have these kinds of restrictions: you cannot call methods before they are declared, the first method call for the derived functions determines the type, the compilation order affects the type inference and etc.