A small, statically-typed functional scripting language for Rust hosts. Whole-module type inference, Rust-style syntax, no ambient I/O.
Created by Enlightware GmbH for use in Candli, an educational game engine that teaches children STEAM through visual programming. Ferlium powers Candli's advanced script blocks: end users write logic the game engine loads, compiles, and runs.
A small Ferlium program:
fn classify(n) {
if n < 0 {
"negative"
} else if n == 0 {
"zero"
} else {
"positive"
}
}
[-1, 0, 1, 2] |> map(classify)
classify has no type annotations; Ferlium infers (int) -> string, generalising or specialising as needed.
The |> operator pipes the array into map.
map lives in the pre-imported std module.
Embedding it from a Rust host (see minimal example):
use ferlium::{CompilerSession, Path, run_fn_native};
let mut session = CompilerSession::new();
let module_id = session
.compile(
"fn answer() -> int { 42 }",
"demo.fer",
Path::single_str("demo"),
)
.unwrap()
.module_id;
let result: isize = run_fn_native!(&session, module_id, "answer", [] -> isize).unwrap();
assert_eq!(result, 42);The same CompilerSession API is what powers the Playground, the REPL, the benchmark suite (benches/benches.rs), and the WebAssembly playground (playground/).
- Type inference with generics. Functions are automatically generalised — write
fn add(a, b) { a + b }and Ferlium infers a polymorphic numeric signature; you get parametric polymorphism for free, with no annotations required. The engine is Hindley–Milner-style with constrained types, and partial annotations with_holes are supported where they aid readability. - Mutable value semantics. A simplified version of Rust's ownership model, based on recent academic work: conceptually function arguments are either values or mutable references to values on the stack, and a small borrow checker keeps mutation memory-safe. No garbage collector, no Rust-level lifetime annotations.
- Algebraic data, traits, modules. Structural records and tagged unions, recursive algebraic data types (including equirecursive structural types), Rust-style new types over them, traits (type classes) with multi-parameter and associated-type support, row polymorphism, and a
module::name/use module::*;system. - Functional core. First-class and anonymous functions with closures (capture by value), pattern matching for destructuring data, and
|>pipelining for chained transformations. - Host-controlled capabilities. The language has no I/O, no FFI, no filesystem, and no network of its own. Every function a script can call is one the embedding application has explicitly registered. There is no
eval, no dynamic loader — the host is the security boundary. - Compile-time effect tracking. Effects (
read,write,fallible) are inferred per function and propagate through call chains. They show up in inferred signatures and diagnostics, so a reviewer can see at a glance whether a function only computes, touches host state, or might fail. Effects are compile-time information, not a runtime sandbox; runtime isolation is the host's responsibility.
- Online playground: https://enlightware.github.io/ferlium/playground/
- REPL:
cargo run --example ferlium(useprint(string)in REPL to print to console) - Book: https://enlightware.github.io/ferlium/book/
- Chat: ferlium.zulipchat.com
Ferlium occupies a niche similar to Lua, Rhai, Rune, or Boa as a script engine for Rust applications, but it is the only one in that group with full Hindley–Milner type inference and a compile-time effect system. Other engines in this space are dynamically typed (Lua, Rhai, Rune, Boa); some offer optional gradual typing. If your host application benefits from catching type errors before the script runs — and from being able to read at a glance whether a function is pure, reads host state, writes host state, or can fail — Ferlium's tradeoffs are aimed at that case.
The integration story also differs from other Rust-embedded scripting engines:
- No native-type duplication. Ferlium's intermediate representation does not re-declare primitives like
intandboolas language constructs — they remain native Rust types throughout, which keeps host bindings lean. - Direct binding to Rust functions. Native Rust functions are exposed as Ferlium-callable functions through direct binding (interpreted today, with a small ABI planned alongside the SSA/WASM backend) — no value-conversion ritual, no generated glue.
- Native value handles for host objects. Host-owned objects can be wrapped as opaque Ferlium values and threaded through scripts — useful for game-engine entities, GPU resources, file descriptors, and the like — without copying.
Today Ferlium is a tree-walking interpreter. A Gungraun-based benchmark suite tracks instruction counts to catch regressions reliably. SSA lowering is currently in progress; a WebAssembly backend is planned on top of it, with the goal of calling Rust-compiled-to-WebAssembly hosts with no FFI overhead.
Ferlium is pre-1.0; expect breaking changes in syntax, APIs, and standard library.
- The interpreter is tree-walking; SSA lowering and a WebAssembly backend are planned but not implemented.
- The compiler is single-threaded — it currently panics if its interned type-universe lock is contended.
- Dynamic dispatch is out of scope.
- No file-based module discovery from script source: module organisation is the host's responsibility (see the Modules chapter of the book).
The issue tracker lists planned features.
Design intent: bring the expressive power of ML-family type systems to people who reach for Python or JavaScript — without asking them to learn category theory first. Its inspirations are:
- ML: basic functional concepts (especially this course and this one)
- Rust: syntax
- Haskell: type system
- HM(X) approach to type inference (paper)
- Mutable Value Semantics (especially this paper)
- This series of blog posts
make test-local runs the suite via nextest, which is significantly faster than cargo test.
Install the dependencies once with make install-deps (Valgrind + the Gungraun runner), then run cargo bench. Gungraun measures instruction counts and other Valgrind-based metrics, which catch small optimisations and regressions reliably even in noisy environments.
Crash-oriented fuzz targets live in fuzz/ and are run explicitly with cargo-fuzz; see doc/fuzzing.md.
See CONTRIBUTING.md for how to contribute.
Ferlium is copyright Enlightware GmbH and licensed under the Apache 2.0 license.
"Ferlium" is a trademark of Enlightware GmbH; see TRADEMARK.md for permitted uses.
The tests in tests/language/ constitute the official Ferlium Conformance Suite.