As part of a hobby project, I would like to dynamically create a function that, when supplied an array of floats, it can then perform some arithmetic using the array elements and return a result.
Note that the function will have to be created at runtime.
I believe this can be done by constructing a lambda expression using the FSharp.Quotations.Expr
module.
Taking the following simple example:
fun (arr: float array) -> 2.0 * arr.[0]
I can re-create this using a code quotation such as:
<@ fun (arr: float array) -> 2.0 * arr.[0] @>
Printing this to the console yields:
Lambda (arr,
Call (None, op_Multiply,
[Value (2.0), Call (None, GetArray, [arr, Value (0)])]))
I can see that the GetArray
referred to is that located FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray
.
My question is... How can I create Call (None, GetArray, [arr, Value (0)])
at runtime?
My (embarassing) attempt so far is:
open System
open FSharp.Quotations
let arr = Var("arr", typeof<float array>, false)
let getArray = FSharp.Core.LanguagePrimitives.IntrinsicFunctions.GetArray.GetType().GetMethods().[0]
Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
This yields the following exception:
System.ArgumentException: 'Type mismatch when building 'args': invalid parameter for a method or indexer property. Expected 'System.Object[]', but received type 'System.Double[]'.
Given that GetArray
looks to be a generic function, it's not clear to me why this is happening.
I'm clearly misunderstanding something rather fundamental here!
Thank you for any advice you can give.
I think the problem here is that you need to obtain the MethodInfo
for GetArray
itself, which requires reflection. I'm not sure if there's an easy way to do this, but the following seems to work:
let arr = Var("arr", typeof<float array>, false)
let getArray =
let asm = System.Reflection.Assembly.Load("FSharp.Core")
let typ = asm.DefinedTypes |> Seq.find (fun typ -> typ.Name = "IntrinsicFunctions")
let getArrayGeneric = typ.GetMethod("GetArray")
getArrayGeneric.MakeGenericMethod(typeof<double>)
let expr = Expr.Lambda(arr, Expr.Call(getArray, [Expr.Var(arr); Expr.Value(0)]))
printfn "%A" expr // Lambda (arr, Call (None, GetArray, [arr, Value (0)]))
This finds the generic GetArray
method inside the FSharp.Core
assembly, and then instantiates it with double
to obtain a MethodInfo
for GetArray<double>
, which is what you need.