Can I call a function by name in f #?

Is there a way to call a function by name in F #? Given the string, I want to wrest the value of the function from the global namespace (or, in general, this module) and call it. I already know the type of function.

Why do I want to do this? I am trying to work with fsi without having the --eval option. I have a script file that defines many int β†’ () functions, and I want to execute one of them. For instance:

fsianycpu --use:script_with_many_funcs.fsx --eval "analyzeDataSet 1" 

My thought was to write a trampoline script, for example:

 fsianycpu --use:script_with_many_funcs.fsx trampoline.fsx analyzeDataSet 1 

To write "trampoline.fsx", I will need to find the function by name.

+6
source share
3 answers

There is no built-in function for this, but you can implement it using .NET reflection. The idea is to search for all types available in the current assembly (the current code is compiled here) and dynamically call the method with the corresponding name. If you got this in a module, you will also need to check the type name.

 // Some sample functions that we might want to call let hello() = printfn "Hello world" let bye() = printfn "Bye" // Loader script that calls function by name open System open System.Reflection let callFunction name = let asm = Assembly.GetExecutingAssembly() for t in asm.GetTypes() do for m in t.GetMethods() do if m.IsStatic && m.Name = name then m.Invoke(null, [||]) |> ignore // Use the first command line argument (after -- in the fsi call below) callFunction fsi.CommandLineArgs.[1] 

This is done in the welcome world when called:

 fsi --use:C:\temp\test.fsx --exec -- "hello" 
+6
source

You can use reflection to get functions like MethodInfo by the name of the FSharp function

 open System open System.Reflection let rec fsharpName (mi:MemberInfo) = if mi.DeclaringType.IsNestedPublic then sprintf "%s.%s" (fsharpName mi.DeclaringType) mi.Name else mi.Name let functionsByName = Assembly.GetExecutingAssembly().GetTypes() |> Seq.filter (fun t -> t.IsPublic || t.IsNestedPublic) |> Seq.collect (fun t -> t.GetMethods(BindingFlags.Static ||| BindingFlags.Public)) |> Seq.filter (fun m -> not m.IsSpecialName) |> Seq.groupBy (fun m -> fsharpName m) |> Map.ofSeq |> Map.map (fun kv -> Seq.exactlyOne v) 

Then you can call MethodInfo

 functionsByName.[fsharpFunctionNameString].Invoke(null, objectArrayOfArguments) 

But you probably need more work to parse your string arguments using MethodInfo.GetParameters() types as a hint.

+3
source

You can also use FSharp.Compiler.Service to create your own fsi.exe with the eval flag

 open System open Microsoft.FSharp.Compiler.Interactive.Shell open System.Text.RegularExpressions [<EntryPoint>] let main(argv) = let argAll = Array.append [| "C:\\fsi.exe" |] argv let argFix = argAll |> Array.map (fun a -> if a.StartsWith("--eval:") then "--noninteractive" else a) let optFind = argv |> Seq.tryFind (fun a -> a.StartsWith "--eval:") let evalData = if optFind.IsSome then optFind.Value.Replace("--eval:",String.Empty) else String.Empty let fsiConfig = FsiEvaluationSession.GetDefaultConfiguration() let fsiSession = FsiEvaluationSession(fsiConfig, argFix, Console.In, Console.Out, Console.Error) if String.IsNullOrWhiteSpace(evalData) then fsiSession.Run() else fsiSession.EvalInteraction(evalData) 0 

If the above was compiled into fsieval.exe , it could be used as such

 fsieval.exe --load:script_with_many_funcs.fsx --eval:analyzeDataSet` 1 
+1
source

All Articles