Skip to main content
Version: 0.60.1

Register Extern Functions

Externs are host functions exposed to scripts.

Each extern defines:

  • name,
  • type scheme,
  • arity,
  • implementation.

Minimal pattern

{ Name = "My.add"
Scheme = Forall([], TFun(TInt, TFun(TInt, TInt)))
Arity = 2
Impl = fun ctx args -> ... }

Why schemes matter

Type schemes are injected into type inference, so script calls are type-checked before evaluation.

Real function injection example

open FScript.Language

let slugifyExtern =
{ Name = "Host.slugify"
Scheme = Forall([], TFun(TString, TString))
Arity = 1
Impl =
fun _ args ->
match args with
| [ VString s ] ->
s.Trim().ToLowerInvariant().Replace(" ", "-")
|> VString
| _ ->
failwith "Host.slugify expects one string argument" }

Use it with runtime externs:

open FScript.Runtime

let hostContext =
{ HostContext.RootDirectory = "."
DeniedPathGlobs = [] }

let externs = slugifyExtern :: Registry.all hostContext

Higher-order externs (callback into script)

When an extern receives a script function as argument, use ExternalCallContext.Apply to invoke it.

let applyTwiceExtern =
{ Name = "Host.applyTwice"
Scheme = Forall([], TFun(TFun(TInt, TInt), TFun(TInt, TInt)))
Arity = 2
Impl =
fun ctx args ->
match args with
| [ fn; VInt n ] ->
let once = ctx.Apply fn (VInt n)
ctx.Apply fn once
| _ ->
failwith "Host.applyTwice expects (int -> int) and int" }

Design tips

  • Keep extern APIs small and explicit.
  • Prefer pure externs when possible.
  • Return option-like outcomes for recoverable failures.
  • Choose stable names; extern names are script-level API surface.