REPL
cure repl drops you into a readline-grade read-eval-print loop
backed by a raw-mode terminal, a pure-function line editor, live
Makeup-powered syntax highlighting, persistent history, incremental
reverse search, Tab completion, and a Marcli-rendered :help. Each
submitted expression is wrapped in a synthetic Repl.M<N> module,
compiled + loaded on the fly, and its main/0 value is echoed back
with an ANSI-highlighted => arrow.
cure repl
The prompt reads cure(N)> where N is the expression counter.
cure(1)> fn add(a: Int, b: Int) -> Int = a + b
=> #Function<...>
cure(2)> :t add(1, 2)
add(1, 2) : Int
cure(3)> :bench add(1, 2) 10000
n=10000 min=1 us avg=2 us p95=3 us max=42 us
Everything on this page landed in v0.24.0. Prior releases used an
IO.gets-backed line-mode loop; the arrow keys printed ^[[A, there
was no persistent history, and no syntax highlighting. The
architecture is split across small modules (Terminal, LineEditor,
History, Search, Highlight, Theme, Completer, Render,
Docs, Markdown) so every piece is individually testable. See the
on-disk reference docs/REPL.md
for the full contract.
When raw mode activates
The REPL defaults to raw-mode when stdin is a tty. When stdin is not
a tty (CI, pipes, | into cure repl, TERM=dumb), it falls back
to the legacy IO.gets loop automatically so test automation is
unaffected. The raw-mode path can be forced off with
Cure.REPL.start(raw: false).
On raw-mode entry, the REPL captures the current stty state and
restores it on every exit path: normal :quit, EOF on an empty
buffer, Ctrl+C on a non-empty buffer, uncaught exception, SIGINT
and SIGTERM. Logger output is quieted to :error for the duration of
the session so stray [warning] lines from dependencies cannot
overwrite the prompt.
Key bindings (emacs mode)
Cursor movement
-
Left/Right– one grapheme -
Ctrl+A/Home– beginning of line -
Ctrl+E/End– end of line -
Alt+B/Ctrl+Left– one word back -
Alt+F/Ctrl+Right– one word forward
Editing
-
Backspace/Ctrl+H– delete previous grapheme -
Delete– delete grapheme under cursor -
Ctrl+D– delete under cursor (or EOF on empty line) -
Ctrl+K– kill to end of line -
Ctrl+U– kill to start of line -
Ctrl+W– kill previous word -
Alt+D– kill next word -
Ctrl+Y– yank most-recent killed text -
Alt+Y– rotate the kill ring (afterCtrl+Y) -
Ctrl+T– transpose adjacent characters -
Alt+T– transpose adjacent words -
Alt+U/Alt+L/Alt+C– upcase / downcase / capitalise word -
Ctrl+_– undo -
Alt+_– redo
History
-
Up/Down– step through history; drafts are preserved -
Ctrl+R– incremental reverse search -
Ctrl+S– forward search (while in search mode) -
Ctrl+G/Esc– cancel search, restore original buffer
Flow
-
Enter– submit (auto-continues on unbalanced brackets) -
;;on its own line – force-submit a multi-line buffer -
Tab– completion (meta-commands, paths, modules, keywords) -
Ctrl+L– clear screen -
Ctrl+C– abort current input; REPL stays running -
Ctrl+Don empty buffer – exit
Vi mode
:mode vi switches to a minimal modal editor. Esc enters normal
mode; i / a / I / A return to insert mode. Supported normal-
mode commands: h, j, k, l, w, b, e, 0, ^, $, x,
D, C, u (undo), Ctrl+R (redo). :mode emacs switches back.
You can also start directly in vi mode via CURE_REPL_MODE=vi cure repl.
Meta-commands
Every meta-command is prefixed with :. Typing : and pressing
Tab cycles through the full list.
Types and effects
-
:t expr(also:type expr) – infer the type ofexpr -
:effects expr– infer the effect set -
:holes– list holes from the last evaluation -
:ast expr– dump the parsed AST -
:fmt expr– pretty-printexpr
Modules and files
-
:load path– compile and load a.curefile -
:reload– reload every previously loaded file -
:use Mod– bringMod‘s exports into scope -
:env– list all imports -
:doc name– show the docstring ofname
Session
-
:reset– forget all bindings, fresh session -
:save path– write the session transcript topath -
:edit– open$EDITOR/$VISUALon the current buffer -
:history [n]– print the lastn(default 20) entries -
:search term– non-interactive history grep -
:clear– clear screen
Timing
-
:time expr– evaluate and report elapsed wall time -
:bench expr [n]– runexprntimes, report min / avg / p95 / max
Appearance
-
:theme dark|light|mono– switch colour theme -
:color on|off– toggle colour output -
:mode emacs|vi– switch editing mode
Quit
-
:help/:h– show help (rendered viaMarcli.render/2) -
:quit/:q/:exit– leave the REPL
Tab completion
Cure.REPL.Completer handles four categories in one pass:
-
Meta-commands. Typing
:and pressingTabcycles through every registered command. -
File paths. Inside
:load,:save,:edit, completes against the filesystem. -
Loaded modules. Inside
:useand:doc, completes against currently-loaded Cure modules. - Literal arguments and Cure keywords. Theme / mode / colour argument values, and the reserved-word list elsewhere.
A single Tab cycles through candidates; repeated Tab presses
rotate. A single candidate is inserted directly.
Live syntax highlighting
Input is piped through Makeup.Lexers.CureLexer and
Marcli.Formatter for ANSI syntax highlighting on every keystroke.
The highlighter caches by buffer hash so repeated redraws do not
re-tokenise.
Three themes ship out of the box:
-
:dark– default, optimised for dark backgrounds. -
:light– inverted palette for light backgrounds. -
:mono– no ANSI colour at all.
:mono is forced automatically when NO_COLOR is set, when stdout
is not a tty, or when TERM=dumb. Toggle at runtime with
:theme dark|light|mono or :color on|off.
History
Entries are persisted to ~/.cure_history via atomic write-and-
rename. Consecutive duplicates are deduplicated; the file is capped
at the most recent 10,000 entries.
Up / Down step through history while preserving the current
draft. Scrolling up and back down returns you to the exact buffer
and cursor position you had before exploring.
Incremental reverse search
Ctrl+R opens an inverse-video status line:
(reverse-i-search)`map': Std.List.map(xs, fn(x) -> x + 1)
- Each keystroke narrows the match.
-
Ctrl+Ragain steps to the next older match. -
Ctrl+Sflips direction to forward search. -
Enteraccepts the match and submits it. -
Tab/ArrowLeft/ArrowRightaccept-and-edit into the main buffer. -
Ctrl+G/Esccancel and restore the original.
Configuration
Programmatic options to Cure.REPL.start/1:
-
:history_path– override~/.cure_history. -
:raw–true,false, or:auto(default); forces the raw- mode editor. -
:theme–:dark,:light,:mono, or:auto. -
:mode–:emacsor:vi.
Environment variables:
-
NO_COLOR– when set, forces the:monotheme. -
CURE_REPL_MODE–vito start in vi mode. -
VISUAL/EDITOR– preferred external editor for:edit. -
TERM=dumb– disables colour and raw-mode editing.
Dependencies
The REPL builds on three companion Hex libraries, all maintained alongside the compiler:
-
makeup_cure– Makeup lexer for the Cure language. -
makeup– the lexer framework itself; declared explicitly soMakeup.Registryis on the load path at runtime. -
marcli– Markdown-to-ANSI renderer and Makeup-token-to-ANSI formatter.:helpand:docoutput are pushed throughMarcli.render/2.
None of these pull in further transitive deps, and all three are optional at compile time: the REPL degrades gracefully when any of them are missing from the load path (colour is dropped, help is rendered as plain text).
Non-goals
- No structural editing / paredit-style features – the editor operates on graphemes, not s-expressions.
-
No remote / networked REPL.
cure repldrives a local BEAM VM. -
No Windows console support. The raw-mode path relies on
sttyand ANSI escape codes; Windows users fall back to the legacy line-mode loop automatically.