Search code examples
c#f#type-systems

How does custom F# types map to CLR types?


I don't know if the title of the question is clear but I am wondering the actual types of custom F# types.

Like in C#, there are value types and reference types. For F#, is there a single type that governs the type system?

Are they all value types (since they are immutable by default)?

Or is/are there completely new types that's nothing like value/reference type(s)?

Any info would be helpful.


Solution

  • F# has various kinds of types. Standard .NET types (that also exist in C#) such as class, interfaces and delegates are mapped directly to the corresponding CLR representation.

    You can also define value type using the Struct attribute (in which case, you get a type corresponding to C# struct) and an enumeration like this:

    [<Struct>]
    type ValueType(a:int) =
       member x.A = a
    
    type Enumeration = 
      | First = 1
      | Second = 2
    

    The remaining F# types are specific to F# and don't have any standard representation in CLR. F# compiler encodes them as a class (or several classes).

    • Record is simply a class with properties corresponding to the fields of the record.
    • Tuple is represented using a generic Tuple<...> class (which is new in .NET 4.0)

    • Function value is represented using a generic FSharpFunc<...> type. This allows F# to implement currying & partial function application, so it is a bit tricky. For example, int -> int -> int would be FSharpFunc<int, FSharpFunc<int, int>>. The type has a single Invoke method that you can use to call it with the first parameter. F# also generates a derived type that contains Invoke method taking all the parameters, which is more efficient and is used most of the time.

    • Discriminated union is represented as a class hierarchy with an abstract base class (with the same name as the union type) and a subclass for each of the cases. For example:

      type Shape = 
        | Rectangle of int * int * int * int
        | Circle of int * int * int 
      

      This would create an abstract base class Shape and two derived concrete classes Rectangle and Circle (actually, nested classes in the Shape class). These derived classes would contain the fields needed to store the properties of the case (e.g. location of a rectangle).

    If you want to know more, you can take a look at the Adventures in F# series by Jomo Fisher.