Adventures in F#–Probing Type Inference

Jomo Fisher--I was curious about type inference in F# and I wondered what would happen if there was really no way for the compiler to infer a type. Consider this function which takes a value and just returns it:

let func n = n

Like last time, I compiled this with fsc.exe. I was expecting a polite error message saying the type of 'n' couldn't be determined. Instead, compilation succeeded. The result was equivalent to this C#:

    public static T func<T>(T n)
        return n;

In retrospect, this makes complete sense. Of course an unknown type should be inferred to be a template type. So far, F# seems to do a good job of being concise for a strongly typed language.


This posting is provided "AS IS" with no warranties, and confers no rights.

Comments (12)

  1. MichaelGiagnocavo says:

    Here’s my vote for a wildcard type specifier in C#!

  2. Alex James says:

    So on the off chance he (the guy I met, not the cat’s father) is reading my blog, I thought I’d point him in the direction of Jomo Fisher’s new post s on F#….

  3. Jon Harrop says:


     let f (a, b) = (b, a)

  4. nelak says:

    Wondering, have you tried debugging for the actual type being infered?

  5. Nelak,

    I haven’t tried VS integration at all at this point. I’m using notepad.exe and fsc.exe. I do plan using VS integration once I understand the language well enough. The reason is that I want to think about and then predict what I would like in an IDE experience for F# first.

  6. nelak says:

    I was not referring to debugging with VS, but to check that calling the method with type inference, like:

    int n;


    would return the type you actually expected to, which should be an int.

    Actually my tests on Framework 2.0 show the type being infered is Object, and has been acknowledge  as an existing bug in orcas beta2 also.

    My guess is the issue probably spreads to other  implementations, so actually you are not getting real type inference, even though I agree the code compiles, but you won’t get the expected behaviour while running it.

  7. nelak says:


    The code should read:

    object obj = new int();

    int n = func(obj);

    This code should fail with an invalid cast exception.

  8. Nelak,

    Though it may be counterintuitive depending on your background with other languages, the second set of code you posted is supposed to be a compile error. The static type passed to func is System.Object and the return variable is System.Int32. By the definition of the function they must be the same. Template parameter resolution happens at compile time in C# not runtime. All the compiler knows at compile time is that ‘obj’ is type System.Object.

  9. nelak says:

    Maybe the example it’s not as intuitive as I would like, in the bug I posted on Connect the issue can be seen more clearly, actually I don’t know if other implementations of the language are affected by this issue, I code in C# and I know it exists, the idea was to show, that internally at runtime, even though you are passing an int, it will be resolved as Object inside the method, because there hasn’t been any explicit casting.

    The code should be:

    object obj = new int();


    public static T func<T>(T n)



           return n;


    Thank you for your time, maybe this helps out to clear the issue.

  10. Hi Nelak,

    Unfortunately, I can’t access the attachment on that connect bug and the rest of the bug doesn’t stand on its own without it. I did run your code above and it prints 0 as expected for me. Do you see a different result?

  11. Jomo Fisher– Easily my favorite feature of F# so far is the combination of discriminated union and pattern

  12. Joel says:

    Right, the "usual" type inference algorithm (Hindley-Milner) tries to find a specific type for each variable.  But if it reaches the end of the expression being checked, it converts all variables with unknown type to "polymorphic" variables, meaning the programming variable works "for all types T".

    This works great for container code, because you just write your code to move the opaque data around, put it into the container, take it out of the container, and voila, it works for containers of all kinds of data.  The theoretical problems come about when you need to do a "little bit" of work with the data itself: compute a hash function, compare two items for equality.  Then you need to do something to still keep the code generic, such as passing in the functions to do the work.  That can get tedious too, so the work goes on to find a sweet spot in language design where you don’t need to declare more than is necessary.

Skip to main content