Mino: Bringing Clojure-Inspired Ergonomics to Portable C99
In the world of embeddable scripting languages, developers often face a trade-off between the lightweight nature of languages like Lua and the powerful data-processing ergonomics of modern functional languages. Mino seeks to bridge this gap by providing a Clojure-inspired Lisp written in portable C99, designed specifically to be dropped into existing host applications with minimal friction.
By combining a small memory footprint with persistent immutable data structures and a capability-based security model, Mino offers a compelling alternative for developers needing a flexible, sandboxed scripting layer without the overhead of a JVM or a heavy runtime.
A Tiered Approach to Embedding
Mino is designed around the concept of "tiers," allowing developers to choose the exact balance between footprint and functionality they require. This modularity ensures that the language remains "small enough to drop in" while remaining "fast enough to be useful."
1. Floor (The Minimum)
This is the leanest version of the runtime, focusing on the absolute essentials: the reader, evaluator, garbage collector, persistent collections, numeric operations, and foundational macros. It excludes the core.clj evaluation, resulting in a tiny footprint of approximately 613 KB and an incredibly fast initialization time of 0.23 ms.
2. Standard (Full Clojure Surface)
Building upon the Floor tier, the Standard version adds the features a Clojure scripter expects, including regex, bignum, multimethods, protocols, and transducers. This tier increases the footprint slightly to 728 KB with an initialization cost of 5.3 ms. Notably, this tier remains a sandbox; it has no I/O capabilities unless the host explicitly opts into them.
3. Standalone (The Full Suite)
The Standalone tier is the complete package, including I/O, filesystem access, Software Transactional Memory (STM), agents, async capabilities, the bundled clojure.* stdlib, and a REPL. At 987 KB and a 5.5 ms init time, it provides the full experience of a Clojure-like environment.
Performance and Portability
One of Mino's primary strengths is its cold-start performance. On x86_64 Linux, the Floor tier's wall-time cold start (median over 50 runs) is 1.04 ms. This puts it in the same league as Lua 5.5 (0.78 ms) and ahead of Janet (1.95 ms), making it suitable for environments where process spawn time is critical.
Because it is written in portable C99, Mino can be embedded into virtually any host with a C FFI, including C++, Rust, Go, Java, .NET, Swift, and Zig. It requires no external runtime, no JIT, and no daemon; the host process maintains total control over the execution.
Architectural Pillars
Isolated Runtimes and Memory Safety
Each Mino runtime operates as its own failure domain with a dedicated heap and garbage collector. To prevent shared mutable state and potential race conditions, data moving between runtimes is handled via deep copies. This architecture allows a single process to host multiple tenants in complete isolation.
Capability-Gated Interop
Security is baked into the runtime's design. A fresh Mino runtime starts with zero capabilities—no filesystem access, no network access, and no ambient state. The embedder must explicitly opt into each capability and register the specific host types and methods the scripts are permitted to call.
Clojure-Inspired Ergonomics
For the scripter, Mino provides the high-level abstractions that make Clojure powerful:
- Persistent Immutable Data Structures: Eliminates aliasing bugs and "who mutated my table" surprises.
- Lazy Sequences: Allows for efficient processing of potentially infinite data streams.
- Tail-Call Optimization: Ensures that recursive functions (like
drainoperations) can run in constant stack space. - REPL-Driven Workflow: Supports live inspection and runtime configuration changes without needing to recompile the host application.
Practical Implementation: The Three-Role Workflow
Mino defines a clear separation of concerns between three primary roles:
- The Application Developer: Embeds the runtime and defines the security boundaries by opting into specific capabilities.
- The C++ Engineer: Exposes host types to the script. Host objects are wrapped as type-tagged handles with automatic GC cleanup. Because the API uses direct value pointers rather than stack indices, it eliminates common off-by-one indexing errors associated with traditional C-based embedding APIs.
- The Scripter: Writes the business logic using the Lisp syntax. They interact with host objects as if they were native Mino types, utilizing keywords as data accessors and collections as callable predicates.
Potential Use Cases
Given its design, Mino is particularly well-suited for:
- Rules Engines & Policy: Exposing host state to Mino predicates for declarative business logic.
- Plugin Systems: Loading user-authored scripts with strictly controlled resource limits and no ambient access.
- Data Pipelines: Composing
map,filter, andreduceover persistent collections with structural sharing. - Game Scripting: Providing a programmable console for players with sandboxing and step limits to prevent infinite loops or crashes.
- Interactive Consoles: Embedding an in-app REPL for live debugging and runtime configuration.