Skip to content

enlightware/ferlium

Repository files navigation

Ferlium

Build Status

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.

Quick look

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/).

Why Ferlium

  • 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.

Getting started

How Ferlium compares

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.

Embedding approach

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 int and bool as 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.

Performance roadmap

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.

Current limitations

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 philosophy

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:

Developing Ferlium

Running tests

make test-local runs the suite via nextest, which is significantly faster than cargo test.

Running benchmarks

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.

Fuzzing

Crash-oriented fuzz targets live in fuzz/ and are run explicitly with cargo-fuzz; see doc/fuzzing.md.

Contributing

See CONTRIBUTING.md for how to contribute.

License and trademark

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.

About

A small, statically-typed functional scripting language for Rust hosts. Whole-module type inference, Rust-style syntax, no ambient I/O.

Resources

License

Contributing

Stars

Watchers

Forks

Contributors