Search code examples
.netf#tuplesbase-class-library

Tuple.Create in F#


I noticed a quite weird behavior of the System.Tuple.Create method in F#. When looking at the MSDN documentation it indicates that the return type is of System.Tuple<T>. However when using this method in F# all overloads except Tuple.Create(T) will return 'T1 * 'T2. Obviously invoking the Tuple<T> constructor will return Tuple<T>. But I don't understand how the return type of the Tuple.Create is different in F#.


Solution

  • The tuple type of F# (a syntactic tuple) is compiled as System.Tuple<..>. So they are the same type at .NET level but for F# type system they are different types: the type of a syntactic tuple will not match the type of a System.Tuple<..> but their runtime type will be the same.

    You can find a detailed description in the F# spec

    The example with new System.Tuple<'t>() does not return a syntactic tuple, probably because you are instantiating explicitly a specific type and you should get back exactly that.

    Here are some tests:

    let x = new System.Tuple<_,_>(2,3) // Creates a Tuple<int,int>
    let y = System.Tuple.Create(2,3)   // Creates a syntactic tuple int * int
    
    let areEqual = x.GetType() = y.GetType() // true
    
    let f (x:System.Tuple<int,int>) = ()
    let g (x:int * int) = ()
    
    let a = f x
    let b = g y
    
    // but
    
    let c = f y 
    //error FS0001: The type 'int * int' is not compatible with the type 'Tuple<int,int>'
    
    let d = g x
    // error FS0001: This expression was expected to have type int * int but here has type Tuple<int,int>  
    

    So, at compile time they are different but at runtime they are the same. That's why when you use .GetType() you get the same result.