F# under the covers XVIII -- lambdas and closures
Consider this code, using named and anonymous inner functions
let F1 l = let aux i = i + 1 let FI li = let rec FII lii acc = match lii with | [] -> acc | x :: xs -> FII xs (aux acc) FII li 0 l |> List.map (fun i -> (string i).Length)
The inner functions are compiled as FSharpFunc
objects, with values closed over being injected as constructor arguments.
Before .net 5.0.200, this would make function F1
look like
public static FSharpList<int> F1<a>(FSharpList<a> l) { FSharpFunc<int, int> aux = new aux@9(); FSharpTypeFunc FI = (FSharpTypeFunc)(object)new FI@11(aux); return ListModule.Map<a, int>((FSharpFunc<a, int>)new F1@17<a>(), l); }
With .net 5.0.200, the fact that some of the inner functions -- like aux
above -- are pure, closing over nothing, has been taken account of, and needless new object creation is avoided, in the same way that C# lambdas have long been cached after first use.
public static FSharpList<int> F1<a>(FSharpList<a> l) { FSharpFunc<int, int> aux = aux@9.@_instance; FSharpTypeFunc FI = (FSharpTypeFunc)(object)new FI@11(aux); return ListModule.Map<a, int>((FSharpFunc<a, int>)F1@17<a>.@_instance, l); }
where the aux
and F1@17
functions -- the latter being the anonymous function used by List.map
-- are referenced through a class internal static readonly
value, rather than having to create a new instance every time.