Skip to main content
Open source · Embeds in .NET and JavaScript

Change the rules.
Keep the host.

Load .fss files, check their types, and run them inside your process. Your application decides which functions scripts can call.

$dotnet tool install --global MagnusOpera.FScript
Static type inferencePattern matchingExplicit host functions
YOUR APP
pricing.fss in-process
type Customer =
  { Name: string
    Plan: string
    Seats: int }

[<export>] let quote (customer: Customer) =
  let basePrice =
    match customer.Plan with
    | "scale" -> 29
    | "team" -> 19
    | _ -> 9

  basePrice * customer.Seats
type checkedFScript
LOAD .FSSCHECK TYPESCALL EXPORTSREGISTER EXTERNSLOAD .FSSCHECK TYPES

Why embed a language?

A pricing change should not require a new application build.

Put changeable rules in .fss files. FScript checks them before execution, while your host keeps ownership of the database, network, filesystem, and other I/O.

01Runs in process
No service to operate.
02Checks types first
Errors stop before evaluation.
03Calls registered externs
The host controls I/O.

What the code looks like

Typed data.
Direct code.

Functions, records, unions, and pattern matching. No class hierarchy required.

01

Name every outcome

Use records and unions to describe the data a rule accepts and every result it can return.

type Decision =
  | Approve of int
  | Review of string
  | Decline

let decide score =
  match score with
  | n when n > 80 -> Approve 5000
  | n when n > 55 -> Review "manual"
  | _ -> Decline
02

Choose what scripts can call

Register typed host functions for the exact operations a script needs. Leave everything else out.

let notifyLateInvoice invoice =
  match invoice.Status with
  | "late" ->
      Host.notify invoice.Owner
        $"Invoice {invoice.Id} is late"
  | _ ->
      ()
03

Call exports from your app

Load a script, find its exported functions, and invoke them by name from .NET or JavaScript.

type Event = { Kind: string }

[<export>] let route (event: Event) =
  match event.Kind with
  | "signup" -> "onboarding"
  | "payment.failed" -> "billing"
  | "churn.risk" -> "success"
  | _ -> "archive"

The host sets the boundary

Scripts get what
you register.

A script cannot query your database or send an HTTP request unless your host gives it a function that does so. Each extern has a name and a checked type signature.

  • 01 Register the externs the script needs
  • 02 Load and type-check the source
  • 03 Invoke a named export
See the embedding model
YOUR HOST.NET / JS
DatabaseHTTPEvents
typed externsexplicit exports
EMBEDDEDFScript
DataRulesExports
CAPABILITY BOUNDARY

Start with running code

Try one rule. Then
wire it into your host.

The browser sandbox runs without an install. When the syntax makes sense, follow the host example for .NET or JavaScript.

FScript mascot

Run the first script

See if FScript fits
your host.