This document describes the host external-function model, built-in extern catalog, and extension points.
An external function is represented by:
Name: stringScheme: Scheme(polymorphic type scheme)Arity: intImpl: ExternalCallContext -> Value list -> Value
Registry entrypoint:
FScript.Runtime.Registry.all : HostContext -> ExternalFunction listHostContextincludes:RootDirectory : stringDeniedPathGlobs : string list(root-relative glob patterns hidden/blocked inside root)
- Optional plugin protocol marker:
[<FScript.Runtime.FScriptExternProvider>]onpublic staticprovider methods in user assemblies- supported method signatures:
unit -> ExternalFunction listHostContext -> ExternalFunction list
Interpreter integration:
- Extern schemes are injected into type inference environment.
- Extern values are injected into runtime environment as curried
VExternal.
LSP integration:
- The language server injects runtime extern schemes (
Registry.all) during document inference. - Hover/completion/signature help expose callable signatures for injected runtime functions and prelude stdlib functions.
- Injected stdlib functions use named-argument signatures when parameter names are available.
- Go-to-definition for stdlib injected functions resolves to readonly virtual stdlib sources (
fscript-stdlib:///...).
Higher-order externs are implemented in runtime extern modules and receive an ExternalCallContext.
ExternalCallContext.Apply is a function pointer provided by the evaluator so extern code can apply script closures and curried functions without re-implementing evaluation.
Fs.readText : string -> string optionFs.exists : string -> boolFs.kind : string -> FsKindFs.createDirectory : string -> boolFs.writeText : string -> string -> boolFs.combinePath : string -> string -> stringFs.parentDirectory : string -> string optionFs.extension : string -> string optionFs.fileNameWithoutExtension : string -> stringFs.glob : string -> string list optionFs.enumerateFiles : string -> string -> string list option- Denied-path policy:
Fs.globandFs.enumerateFileshide denied entries.Fs.readText,Fs.writeText, andFs.createDirectorythrow an eval error on denied targets.Fs.existsreturnsfalseandFs.kindreturnsFsKind.Missingfor denied targets.
Regex.matchGroups : string -> string -> string list option
Hash.md5 : string -> string optionGuid.new : 'a -> string option(dummy argument to preserve call shape)
List.*,Option.*, andMap.*helpers are provided by the embedded prelude inFScript.Language.- Scalar conversion helpers are built into the language runtime:
Int.tryParse : string -> int optionFloat.tryParse : string -> float optionBool.tryParse : string -> bool optionInt.toString : int -> stringFloat.toString : float -> stringBool.toString : bool -> stringString.replace : string -> string -> string -> string- argument order:
oldValue -> newValue -> source
- argument order:
String.indexOf : string -> string -> int option- argument order:
value -> source
- argument order:
String.toLower : string -> stringString.toUpper : string -> stringString.substring : int -> int -> string -> string option- argument order:
start -> length -> source
- argument order:
String.concat : string -> string list -> stringString.split : string -> string -> string list- argument order:
separator -> source
- argument order:
print : string -> unitis a built-in language function (not a host extern).- Runtime externs focus on host/system capabilities.
FsKindis provided by stdlib as:FsKind.File of stringFsKind.Directory of stringFsKind.Missing
- See
stdlib-functions.mdfor the full stdlib function reference.
Json.deserialize : type -> string -> 'a optionJson.serialize : 'a -> string optionXml.queryValues : string -> string -> string list option- argument order:
query -> xmldoc - query subset:
.../@attrand.../text()selectors only - supports simple element paths with optional leading
//descendant step - returns
Some []when query is valid but no values match - returns
Nonefor XML parse failure or invalid/unsupported query syntax
- argument order:
- Type/arity mismatch in extern calls raises eval errors.
- Many data/IO externs return
Noneon operational failures (parse/IO/regex failures), rather than throwing. Xml.queryValuesdoes not support XML namespaces.
Recommended steps:
- Add extern in a host module (
src/FScript.Runtime/*Externs.fs) withName/Scheme/Arity/Impl. - Register in
Registry.all. - For higher-order externs, use
ExternalCallContext.ApplyfromImplto invoke script functions. - Add tests in:
tests/FScript.Runtime.Testsfor direct module behavior.tests/FScript.Language.Tests/HostExternTests.fsfor interpreter integration.
- The
fscriptCLI can inject externs from user assemblies via:--extern-assembly <path>(repeatable)--no-default-externs(disablesRegistry.all)
- Provider discovery:
- CLI loads each unique assembly path once.
- It discovers methods annotated with
[<FScriptExternProvider>]. - It invokes each discovered provider once per CLI process startup.
- Conflicts:
- Any duplicate external function name across defaults + user providers is a fatal CLI error.
- Runtime constraints:
- If runtime assembly loading is unavailable (for example native AOT),
--extern-assemblyis rejected with a clear CLI error.
- If runtime assembly loading is unavailable (for example native AOT),
- External names are part of source-level API; renaming is breaking.