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

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

The comment thread mentioned that F # can declare functions that can take parameters of several possible types, such as a string or an integer. This will look like method overloading 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 one, specific list of parameter types. However, since functions from F # are first-class citizens, it looks like you should be able to pass such a function, and the only way to compile it in CIL is to use delegates.

So how does F # compile this in CIL?

+5
source share
4 answers

When you write C # and you need a function that can take several different sets of parameters, you simply create method overloads:

string f(int x)
{
    return "int " + x;
}
string f(string x)
{
    return "string " + x;
}
void callF()
{
    Console.WriteLine(f(12));
    Console.WriteLine(f("12"));
}
// there no way to write a function like this:
void call(Func<int|string, string> func)
{
    Console.WriteLine(func(12));
    Console.WriteLine(func("12"));
}

The function is callFtrivial, but my built-in syntax for the function calldoes not work.

When you write F # and you need a function that can take several different sets of parameters, you create a discriminatory union that can contain all the different sets of parameters, and you make one function that takes this 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 the only function, it fcan be used like any other value, so in F # we can write callFand call f, and both do the same.

, F # Either, ? , :

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; }
    }
}

call:

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

f :

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

, Either # (duh!), , .

+6

, , F #.

F # , #. someObj.MethodName someType.MethodName. , , #. :

type T() =
    member this.M(x:int) = ()
    member this.M(x:string) = ()
let t = new T()
// these are all ok, just like C#
t.M(3)
t.M("foo")
let f : int -> unit = t.M
let g : string-> unit = t.M
// this fails, just like C#
let h = t.M // A unique overload for method 'M' could not be determined 
            // based on type information prior to this program point.

F # . :

let foo(x:int) = ()
let foo(x:string) = ()  // Duplicate definition of value 'foo'

, "" foo, . .

, , , , , . F # inline, " ", , , " T, - Bar" -. CIL. , , inline, , , .

let inline crazy(x) = x.Qux(3) // elided: type syntax to constrain x to 
                               // require a Qux member that can take an int
// suppose unrelated types U and V have such a Qux method
let u = new U()
crazy(u) // is expanded here into "u.Qux(3)" and then compiled
let v = new V()
crazy(v) // is expanded here into "v.Qux(3)" and then compiled

, , , , , call-. "" crazy , CIL, F # callsite, call-, , ++.

( / - . inline +, , , " int s" " float s" -. ML- (F # OCaml) , , , + int s, , +., float s. F #, + inline, F #, + . , / .)

+11

, , F # , . ,

let inline f x a = (^t : (member Method : ^a -> unit)(x,a))

f, x ^t a ^a, ^t Method, ^a unit (void #), . inline, , , . , f , , ^t ^a , ( ^ ' sigil).

f :

type T() = 
  member x.Method(i) = printfn "Method called with int: %i" i

List.iter (f (new T())) [1; 2; 3]

Method . f ,

List.iter ((fun (x:T) a -> x.Method(a)) (new T())) [1; 2; 3]

, , -, (# - "" ?), , , . , , , . , , , : F #:

type FunctionType = 
| NoArgument of (unit -> unit)
| ArrayArgument of (obj[] -> unit)

let doNothing (arr:obj[]) = ()
let doSomething () = printfn "'doSomething' was called"

let mutable someFunction = ArrayArgument doNothing
someFunction <- NoArgument doSomething

//now call someFunction, regardless of what type of argument it supposed to take
match someFunction with
| NoArgument f -> f()
| ArrayArgument f -> f [| |] // pass in empty array

CIL; NoArgument ArrayArgument FunctionType, . , , , 100% - . # - , , ..

, ; F # , , , .

+4

, ... F # FSharpFunc . F # , , , , # - . - F # . , ---------has-addition-operator, , . @kvb .

+1
source

All Articles