How does F # compile functions that can take several different types of parameters in IL?


I know virtually nothing about F#. I don’t even know the syntax, so I can’t give examples.

It was mentioned in a comment thread that F# can declare functions that can take parameters of multiple possible types, for example a string or an integer. This would be similar to method overloads in C#:

public void Method(string str) { /* ... */ }
public void Method(int integer) { /* ... */ }

However, in CIL you cannot declare a delegate of this form. Each delegate must have a single, specific list of parameter types. Since functions in F# are first-class citizens, however, it would seem that you should be able to pass such a function around, and the only way to compile that into CIL is to use delegates.

So how does F# compile this into CIL?

When you're writing C# and you need a function that can take multiple different parameter sets, you just create method overloads:

string f(int x)
    return "int " + x;
string f(string x)
    return "string " + x;
void callF()
// there's no way to write a function like this:
void call(Func<int|string, string> func)

The callF function is trivial, but my made-up syntax for the call function doesn't work.

When you're writing F# and you need a function that can take multiple different parameter sets, you create a discriminated union that can contain all the different parameter sets and you make a single function that takes that union:

type Either = Int of int
            | String of string
let f = function Int x -> "int " + string x
               | String x -> "string " + x

let callF =
    printfn "%s" (f (Int 12))
    printfn "%s" (f (String "12"))

let call func =
    printfn "%s" (func (Int 12))
    printfn "%s" (func (String "12"))

Being a single function, f can be used like any other value, so in F# we can write callF and call f, and both do the same thing.

So how does F# implement the Either type I created above? Essentially like this:

public abstract class Either
    public class Int : Test.Either
        internal readonly int item;
        internal Int(int item);
        public int Item { get; }
    public class String : Test.Either
        internal readonly string item;
        internal String(string item);
        public string Item { get; }

The signature of the call function is:

public static void call(FSharpFunc<Either, string> f);

And f looks something like this:

public static string f(Either _arg1)
    if (_arg1 is Either.Int)
        return "int " + ((Either.Int)_arg1).Item;
    return "string " + ((Either.String)_arg1).Item;

Of course you could implement the same Either type in C# (duh!), but it's not idiomatic, which is why it wasn't the obvious answer to the previous question.