I have created a NuGet package that lets you easily use F# scripting in your apps.
What is this all about? ¶
In two previous posts (1, 2) I showed you how to host the F# compiler in your own applications.
This time I wanted to reveal the result of those experiments.
I wanted to enable F# scripting in a few of my apps. This requirement made me create the Fsc Host project. The goal was to create a light-weight reusable abstraction over
FSharp.Compiler.Service focused on compiling scripts and consuming them by the hosting application. The NuGet package is available here. The project is still in the experimental phase (version 0) so the API and available features may change, (and the changes may be breaking so bear that in mind before using it in your projects).
How it works ¶
Fsc-Host requires the .NET SDK so the easiest way to use it is to distribute your application as a container image. This way you can ensure it always works as expected. If you reference NuGet packages in your script it will need connectivity to a NuGet source (whether it is nuget.org or your private NuGet feed). To use Fsc-Host you just need to reference the Queil.FSharp.FscHost package and write a few lines of code.
The source code for this example is available here.
script.fsx - the script you consume ¶
let helloFromScript name = sprintf "HELLO FROM THE SCRIPT, %s" name // access it in the host app by: // Property<string -> string>.Path "Script.helloFromScript" let myPrimes = [2; 3; 5] // access it in the host app by: // Property<int list>.Path "Script.myPrimes"
Program.fs - the hosting application ¶
let (export, myPrimes) = File "script.fsx" |> CompilerHost.getMember2 Options.Default (Member<string -> string>.Path "Script.helloFromScript") (Member<int list>.Path "Script.myPrimes") |> Async.RunSynchronously
There are a few things that need to be considered:
Default module name - if the script doesn’t define
namespace, all the
letbindings will be in the default top level module. The name of the module is usually a PascalCase version of the script file name (bar the extension). Also if the file name contains special characters it might require some debugging to figure out what it gets transformed to). So
Consuming functions -
it requires an additional step which is to convert a function to a function value. Function values can be easily downcast to F# functions in the hosting application. Hence the binding ofNow it is supported as is.
Wrapping up ¶
I hope you now have a high-level understanding of what I tried to achieve in the project. I hope some documentation will follow shortly. Until it happens feel free to browse through the tests to get an idea of what the exposed features are.