You can easily extend your applications by calling functions and retrieving values on run time from dynamically compiled scripts. A bare minimum example (Plugin API):
let plugin (s:string) = printfn $"HELLO: %s{s}"
let myWriter =
plugin<string -> unit> {
load
} |> Async.RunSynchronously
myWriter $"I hereby send the message"
HELLO: I hereby send the message
#r "nuget: ..."
directive#r "paket: ..."
directivefsc-host
as Docker images)This project is still in v0 which means the public API hasn't stabilised yet and breaking changes may happen between minor versions. Breaking changes are indicated in the release notes in GitHub releases.
dotnet new console -lang F# --name fsc-host-test && cd fsc-host-test && dotnet add package Queil.FSharp.FscHost --version 0.16.0
script.fsx
:let helloFromScript name = sprintf "HELLO FROM THE SCRIPT, %s" name
let myPrimes = [2; 3; 5]
Program.cs
:open Queil.FSharp.FscHost
try
// compile a script and retrieve two properties out of it
let getScriptProperties () =
File "script.fsx"
|> CompilerHost.getMember2 Options.Default
(Member<string -> string>.Path "Script.helloFromScript")
(Member<int list>.Path "Script.myPrimes")
let (helloWorld, primesOfTheDay) = getScriptProperties () |> Async.RunSynchronously
let myName = "Console Example"
myName |> helloWorld |> printfn "%s"
printfn "Primes of the day: "
primesOfTheDay |> Seq.iter (printfn "%i")
with
// you can handle more exceptions from the CompilerHost here
| ScriptMemberNotFound(name, foundMembers) ->
printfn "Couldn't find member: '%s'" name
printfn "Found members: "
foundMembers |> Seq.iter(printfn "%s")
dotnet run
:HELLO FROM THE SCRIPT, Console Example
Primes of the day:
2
3
5
The public API of this library comes in three flavours:
plugin - high-level, declarative, and it's the recommended API to use. Example
basic - the CompilerHost.getMember
functions family. They take a script and options as the input and return a tuple of extracted member(s).
Example
compile'n'extract - CompilerHost.getAssembly
can be used to compile a script into an assembly (which is automatically loaded). Then members can be extracted with Member.get
function. This API gives more flexibility and enables using generic functions.
Example
nuget:
and paket: nuget
in #r
directives as it may result in an error where two versions of the same assembly are resolved. If it is not possible to avoid then the top-level script should specify NuGet-compatible resolution strategies for paket. I.e. strategy: min
and lowest_matching: true
.Fix FSharp.Core package hash locally:
docker run --rm -it -v $(pwd):/build -w /build mcr.microsoft.com/dotnet/sdk:8.0 dotnet restore --force-evaluate