Joseph Ferano 5b1567c49f The transient thunk, so an expression has something to compile to
Item 2 of HANDOFF-x86-redef.md. C-x C-e and every value a break loop computes
are not a redefinition: there is no name to install a body into, so the
expression is wrapped in a function with nowhere to be called from and the
module says "run this once". flan_reload_call is that wrapper, and the agent
dlsyms exactly that spelling.

Its shape is emit_main's rather than a body's: no caller hands it a transfer
channel, so it owns a null cell on its own frame and passes that cell's address
on. Sixteen bytes of frame rather than eight, because rsp has to be 16-aligned
at the call and that is the whole of what the ABI asks of a frame making one.

The thunk itself is excluded from everything else the module does -- no cell, no
publish, no registry slot. There are 4096 slots and an expression evaluated in a
loop would exhaust them, and a module with nothing pointing into it is what lets
the agent unload it at all.

@flan_reload_transient is that claim, under emit.ml's three conditions. The
third is about data rather than text and is the one that can be got wrong in the
dangerous direction: a string literal lives in this module's image, an
expression may store one anywhere it likes, and a global left pointing into an
unmapped image is silent garbage rather than a fault. So the count is kept where
the literals are made -- string_const bumps the same Emit.m.nstr field emit.ml
counts on, and float constants, which are loaded and never retained,
deliberately do not. The install function's own registry name strings go through
string_const too, which is right rather than incidental: a module that interned
a name left something behind.
2026-09-14 10:34:29 +07:00
2024-07-09 21:01:55 +10:00
2026-09-10 14:56:35 +07:00
2026-09-10 14:40:34 +07:00
2026-09-14 07:34:26 +07:00

Flan

Flan

A statically typed Lisp for native games and interactive development.

Flan is an experimental, ahead-of-time compiled Lisp for programs that need predictable memory use and a fast editrun loop. It combines S-expressions, static types, explicit ownership, and a development session that can replace a function in a running program without resetting its state.

It is being built around games, but the interesting part is broader: a compiled language where the running program remains available for inspection, experimentation, and small changes.

In practical terms: you get parentheses, a debugger that would like to have a conversation, and no garbage collector quietly choosing the dramatic moment to join your frame loop.

What it has

  • Native compilation through LLVM, plus an in-progress direct x86-64 backend.
  • C-like data layout: structs, fixed arrays, pointers, slices, and explicit allocation. There is no garbage collector.
  • Owned Vec and Map containers, plus checked moves and borrowing-oriented slice operations.
  • Generics, algebraic unions, enums, macros, packages, defer, and a C FFI.
  • Conditions and restarts for recoverable failures and interactive debugging.
  • A raylib package and a collection of ported raylib examples.
  • Native, WASI, and web build targets. The cross targets are useful but less complete than the native development workflow.

The project is exploratory software, not a stable language release. Some features are deliberately refused while their semantics are still undecided; the compiler aims to say why rather than quietly accepting a partial version. It has opinions, but at least they arrive as error messages.

Quick start

Building requires a current OCaml/Dune toolchain, LLVM/Clang, and the native C toolchain. Raylib is only needed for programs that use the bundled graphics package.

dune build
dune exec ./bin/main.exe -- run web/examples/hello.flan

To build a standalone native executable:

dune exec ./bin/main.exe -- build web/examples/hello.flan -o hello
./hello

The falling-sand demo uses raylib:

dune exec ./bin/main.exe -- run sand.flan

Once you are iterating regularly, put the built executable on your PATH if you want to use the shorter flan commands shown below.

The live development loop

Start a long-lived development session:

flan dev sand.flan

The program runs normally and publishes a local socket beside the source file. The bundled Emacs mode can attach to it, evaluate expressions in the live process, inspect a stopped program, and recompile a top-level function from the buffer. A body change takes effect on the next call; changing a function's signature is intentionally rejected. The program keeps its state, which is especially nice when you have finally arranged the sand into something almost worth saving.

To set up the mode:

(add-to-list 'load-path "~/path/to/flan/emacs")
(require 'flan-mode)

Then use M-x flan-dev to start and attach, or C-c C-z to attach to a session started in a terminal. The editor workflow is documented in emacs/MANUAL.md.

A small example

(defstruct AssetMissing [id i32])

(defn load-asset [id i32] i32
  (signal (AssetMissing {.id id}))
  100)

(defn asset-or-placeholder [id i32] i32
  (restart-case (load-asset id)
    (use-placeholder [] -1)))

(defn main [] ()
  (handler-bind [(AssetMissing [_] (invoke-restart 'use-placeholder))]
    (println (asset-or-placeholder 7))))

Here a missing asset signals a typed condition. The handler chooses a restart, so execution continues with a placeholder instead of requiring error values to be threaded through every caller. See web/examples/restart.flan for a runnable version.

Commands

flan check <file.flan>                         type-check a program
flan run <file.flan> [args...]                 build and run it
flan build <file.flan> [-o out] [options]      build a native executable
flan dev <file.flan> [-s socket]               start a live development session

Useful build options include --debug, --sanitize, --no-bounds-checks, --x86, and --target=wasm32-wasi|web. run is native-only; cross-built output should be run with an appropriate WASI runtime or browser. A .wasm file is not a tiny native executable in a trench coat.

Project map

License

Flan is released under the MIT License. Third-party material under vendor/ is distributed under its own licenses.

Description
No description provided
Readme MIT 7.5 MiB
Languages
OCaml 67.2%
Emacs Lisp 15.2%
C 10.4%
HTML 2.9%
Standard ML 2.8%
Other 1.5%