This page introduces RoyML/Llaml, a functional programming language that compiles to JavaScript. It covers the project's history as a fork of the archived Roy language, the core design philosophy centered on ML-style type inference, and the current implementation status. For installation and usage instructions, see Getting Started. For architectural details, see Architecture Overview.
Llaml (also known as RoyML) is a statically-typed functional programming language inspired by the ML family (OCaml, Standard ML, F#) that compiles to JavaScript. The language implements Hindley-Milner type inference (Algorithm W), allowing developers to write code without explicit type annotations while maintaining type safety.
The compiler, implemented in JavaScript, transforms .lml source files into executable JavaScript or standalone binaries via the QuickJS runtime. The system provides a unified command-line tool (llml) with subcommands for compilation, execution, and an interactive REPL.
Primary Components:
| Component | Key Code Entities | Purpose |
|---|---|---|
| Lexer | tokenise() in src/lexer.js | Converts source text into token stream |
| Parser | parser.parse() in lib/parser.js generated from src/grammar.js | Constructs AST from tokens |
| Type System | Variable, BaseType, FunctionType, ArrayType, ObjectType, TagType, TupleType, ReferenceType classes in src/types.js | Represents type structure |
| Type Inference | analyse(), unify(), prune(), fresh() in src/typeinference.js | Implements Algorithm W for type checking |
| Code Generator | compile(), compileNodeWithEnvToJsAST() in src/compile.js | Transforms typed AST to JavaScript AST |
| Module System | loadModule(), exportType(), visitImportIntoModule() in src/modules.js src/typeinference.js | Handles open statements and .roym type files |
| Runtime Library | __rml_sys_add, __rml_sys_eq, __rml_print, __rml_open in runtime/runtime.js | Provides primitive operations and I/O |
| Standard Library | std/list.lml std/string.lml std/file.lml std/os.lml std/math.lml | High-level functional utilities |
| CLI/REPL | nodeRepl(), runRoy(), outputBuiltCode() in src/node-repl.js parseCLI() in src/cli.js | Command-line interface and interactive shell |
Sources: README.md1-88 src/lexer.js src/typeinference.js src/compile.js src/modules.js runtime/runtime.js src/node-repl.js src/cli.js
RoyML is a 2025 fork of the Roy programming language, which was archived in 2017. The fork's motivation is to serve as a learning platform for type inference algorithms, structural typing, and the integration of functional and imperative programming concepts.
| Period | Project | Status | Contributors | Repository |
|---|---|---|---|---|
| 2011-2017 | Roy | Archived | Brian McKenna + 29 contributors | github.com/puffnfresh/roy (archived) |
| 2018-2024 | - | Dormant | - | - |
| 2025-Present | RoyML/Llaml | Active | hexaredecimal | github.com/hexaredecimal/royml |
Sources: AUTHORS+HISTORY1-67 README.md43
RoyML follows ML-family language design principles while targeting JavaScript as the compilation target. The core philosophy emphasizes type safety without annotation burden, functional programming patterns, and practical interoperability with JavaScript.
Type Inference Over Annotation: Implements Algorithm W (Damas-Hindley-Milner) to deduce types automatically. Explicit type annotations are optional and used primarily for documentation or external function declarations.
Functional-First with Imperative Escape Hatches: Primary programming model is functional (immutable data, pure functions, pattern matching), but the language aims to support imperative constructs (loops, mutable references) where pragmatic.
Structural Typing: Type compatibility based on structure rather than nominal declarations (work in progress).
JavaScript Interoperability: Generated JavaScript should be readable and interoperable with existing JavaScript code. External functions can be declared with type signatures using #[extern] annotations.
ML Family Aesthetics: Syntax and semantics inspired by OCaml, Standard ML, and F#, including curried functions, algebraic data types, and pattern matching.
Sources: README.md27-39 README.md42-61
RoyML implements the following features as of the current development status:
| Feature | Status | Description | Code Reference |
|---|---|---|---|
| Function Currying | ✅ Complete | Functions automatically curry: f a b desugars to (f(a))(b) | src/compile.js visitCall() |
| Pattern Matching | ✅ Complete | Match expressions with destructuring, guards, exhaustiveness checking | src/compile.js visitMatch() |
| Type Inference | ✅ Complete | Algorithm W with analyse(), unify(), prune(), fresh() | src/typeinference.js |
| Typed External Functions | ✅ Complete | #[extern] annotation for JavaScript FFI with type signatures | src/compile.js annotation handling |
| Annotations | ✅ Complete | #[export], #[extern], #[inline] directives | src/nodes.js annotation nodes |
| Operator Overloading | ✅ Complete | Type-class based with encodeOperatorName() name mangling | src/typeinference.js operator system |
| Binary Compilation | ✅ Complete | QuickJS qjsc integration for standalone executables | Makefile build targets |
| Let-in Expressions | ✅ Complete | Local bindings with lexical scope | src/nodes.js LetIn node |
| Proper Error Messages | ✅ Complete | Source location tracking via src/errors.js | prettyError() function |
| CLI Improvements | ✅ Complete | Subcommand interface: llml run, llml build | src/cli.js and src/node-repl.js |
| Modules | ✅ Complete | open statements, .roym type files, loadModule() | src/modules.js |
| Include Files | ✅ Complete | File inclusion system | Module loading mechanism |
| Mutability (References) | ✅ Complete | Mutable cells via & (create), * (deref), = (assign) | runtime/runtime.js __op_and_, __op_mul_, __op_eq_ |
| Custom stdlib | ✅ Complete | Standard library in std/ with List, IO, Math, File, ADTs | std/list.lml std/in.lml etc. |
| Structural Typing | ✅ Complete | Type compatibility based on structure | Type system in src/types.js |
| Feature | Status | Notes |
|---|---|---|
| Formatter/Pretty Printer | 🚧 WIP | Code formatting utilities |
Sources: README.md9-25 src/compile.js src/typeinference.js runtime/runtime.js
The RoyML compiler follows a classic pipeline architecture transforming source code through multiple stages. This section provides a high-level overview; detailed architectural information is available in Architecture Overview.
Diagram: Source-to-JavaScript transformation stages
Sources: src/lexer.js1-200 lib/parser.js1-100 src/grammar.js1-300 src/typeinference.js1-800 src/compile.js1-1000
<old_str>
The env object is the central data structure threaded through type inference and compilation, maintaining type bindings, operator overloads, and module state.
Diagram: env object properties and accessor functions
Sources: src/typeinference.js src/compile.js src/modules.js </old_str> <new_str>
Diagram: Major subsystems with code entity mappings
Sources: src/cli.js1-100 src/node-repl.js1-400 src/lexer.js1-200 lib/parser.js1-100 src/typeinference.js1-800 src/types.js1-300 src/compile.js1-1000 src/modules.js1-200 runtime/runtime.js1-500 std/list.lml1-100 std/string.lml1-50 std/file.lml1-50 std/printf.lml1-30 std/math.lml1-150
| Category | Entity | File | Purpose |
|---|---|---|---|
| Lexing | tokenise() | src/lexer.js15-180 | Converts source text to token stream |
| Parsing | parser.parse() | lib/parser.js1-50 | Constructs AST from tokens (jison-generated) |
typeparser.parse() | lib/typeparser.js1-50 | Parses type annotation syntax (jison-generated) | |
| Type Inference | analyse() | src/typeinference.js100-600 | Algorithm W implementation, main type checker |
unify() | src/typeinference.js650-750 | Type equation solver with occurs check | |
prune() | src/typeinference.js35-50 | Resolves type variable indirection chains | |
fresh() | src/typeinference.js60-95 | Type instantiation for polymorphism | |
| Type Classes | Variable | src/types.js10-30 | Represents type variables with instance pointer |
BaseType | src/types.js40-60 | Base class for concrete types | |
FunctionType | src/types.js100-130 | Function type a -> b | |
ArrayType | src/types.js140-160 | Array type [a] | |
ObjectType | src/types.js170-200 | Record type {field: Type} | |
TagType | src/types.js220-250 | Algebraic data type with constructors | |
| Code Generation | compile() | src/compile.js50-100 | Entry point for JavaScript emission |
compileNodeWithEnvToJsAST() | src/compile.js150-200 | AST visitor for code generation | |
visitMatch() | src/compile.js500-650 | Pattern matching to conditional logic | |
visitCall() | src/compile.js300-400 | Function call with automatic currying | |
| Modules | loadModule() | src/modules.js20-100 | Loads .lml or .roym files |
exportType() | src/modules.js110-150 | Exports type bindings from modules | |
visitImportIntoModule() | src/typeinference.js750-800 | Handles open statements | |
env.$importCache | src/modules.js15 | Module import cache object | |
| CLI/REPL | parseCLI() | src/cli.js10-80 | Command-line argument parser |
nodeRepl() | src/node-repl.js100-300 | REPL and file execution | |
runRoy() | src/node-repl.js350-450 | Compiles and runs .lml files | |
outputBuiltCode() | src/node-repl.js500-550 | Outputs JavaScript to stdout | |
| Runtime | __rml_sys_add | runtime/runtime.js50-60 | Native addition operator |
__rml_sys_eq | runtime/runtime.js70-80 | Native equality check | |
__rml_print | runtime/runtime.js200-210 | Print function (console.log wrapper) | |
__rml_open | runtime/runtime.js300-350 | File open operation | |
__op_add, __op_mul_ | runtime/runtime.js100-150 | Overloadable operators | |
| AST Nodes | Module | src/nodes.js20-40 | Top-level module node |
Function | src/nodes.js50-80 | Function definition node | |
Let | src/nodes.js90-110 | Let binding node | |
Match | src/nodes.js150-180 | Pattern match expression | |
Call | src/nodes.js200-220 | Function application | |
| Utilities | prettyError() | src/errors.js10-60 | Error formatting with source context |
freeVariables() | src/freeVariables.js5-50 | Free variable analysis | |
tarjan() | src/tarjan.js10-80 | Strongly connected components algorithm |
Sources: src/lexer.js1-200 lib/parser.js1-100 lib/typeparser.js1-100 src/typeinference.js1-850 src/types.js1-300 src/compile.js1-1000 src/modules.js1-200 src/cli.js1-100 src/node-repl.js1-600 runtime/runtime.js1-500 src/nodes.js1-300 src/errors.js1-80 src/freeVariables.js1-60 src/tarjan.js1-100
RoyML supports multiple output formats depending on the use case:
Diagram: CLI command routing to output formats
Sources: src/node-repl.js1-600 src/cli.js1-100 Makefile1-100 runtime/runtime.js1-500
| Mode | Command | Output | Implementation |
|---|---|---|---|
| JavaScript (stdout) | llml build stdout file.lml | JavaScript source to stdout | outputBuiltCode() in src/node-repl.js |
| Standalone Binary | llml build -exe file.lml | Executable file | qjsc via Makefile |
| Direct Execution | llml run file.lml | Program execution on Node.js | nodeRepl() with runMode in src/node-repl.js |
| Interactive REPL | llml run repl | Interactive session | nodeRepl() with REPL mode in src/node-repl.js |
Binary Compilation Details: The llml build -exe mode compiles the Llaml source to JavaScript, then invokes the QuickJS compiler (qjsc) to bundle the generated code with the QuickJS runtime and runtime/runtime.js functions. The resulting binary is self-contained and requires no external JavaScript engine. QuickJS provides std (standard I/O) and os (file system) modules that runtime/runtime.js wraps for portability.
Browser Compilation: The dist/royml.browser.js bundle is generated by esbuild and can be included in web pages, though file system operations from std/file.lml will not be available in the browser environment.
Sources: README.md24 Makefile src/node-repl.js runtime/runtime.js
Development Phase: Active development as of 2025
Maturity Level: Experimental/Learning Project - The project explicitly serves as a learning platform for type inference algorithms, structural typing, and understanding how functional languages integrate imperative concepts like loops and mutable references while maintaining functional paradigms.
Implemented Core Features:
analyse(), unify(), prune(), fresh() in src/typeinference.jsopen statements and .roym type files via src/modules.js& (create), * (dereference), = (assign) operatorsKnown Limitations:
Primary Use Cases:
Example Workflow from README:
This demonstrates the module system (open), list construction (: operator), function composition (|> pipe), and type inference (no annotations needed).
Sources: README.md1-88 AUTHORS+HISTORY1-67 src/typeinference.js src/compile.js
This wiki is organized into the following major sections:
| Section | Purpose |
|---|---|
| Getting Started | Installation, first program, basic CLI usage |
| Architecture Overview | System architecture, component interactions |
| Compilation Pipeline | Detailed compiler internals: lexing, parsing, type checking, code generation |
| Type System | Type definitions, inference algorithm, polymorphism |
| AST Node Types | Complete AST node reference |
| Module System | Module loading, type files |
| Runtime Library | Runtime primitives and operators |
| CLI and REPL | Command-line tools and interactive shell |
| Development Guide | Building, testing, contributing |
| Language Reference | Complete language syntax and features |
Sources: Table of contents provided in prompt
RoyML/Llaml is released under the MIT License with dual copyright:
The project acknowledges 29+ contributors to the original Roy project (2011-2017) and continuing development by hexaredecimal (2025-present).
Sources: LICENSE1-23 AUTHORS+HISTORY1-67
Refresh this wiki
This wiki was recently refreshed. Please wait 3 days to refresh again.