Early preview: syntax, standard library APIs, and tooling may change.

Working ProgramsElm-Style TUI

Elm-Style TUI

Build a tiny terminal app with Elm-style state transitions and an impure shell.

Pure Core, Impure Shell

The shell owns terminal input and output. Application code is plain state plus pure message parsing, updates, and rendering.

Example

The shell owns input and output; state transitions, event parsing, and rendering stay pure.

todo-tui.brp
record Model {value: Int, running: Bool}


union Msg:
	Increment
	Decrement
	Reset
	Quit
	Ignore


func run_app[State, Message](
	initial: State,
	parse_event: pure (String) -> Message,
	step: pure (State, Message) -> State,
	render: pure (State) -> String,
	active: pure (State) -> Bool,
) -> Void:
	-- This shell owns all terminal I/O.
	var state: State = initial
	var eof: Bool = False
	print(render(state))
	while active(state) and not eof:
		match input("cmd> "):
			Some(raw):
				-- The application callbacks are pure, so input and output cannot leak into update or view.
				state = step(state, parse_event(raw))
				print(render(state))
			None:
				eof = True


pure func init() -> Model:
	{value = 0, running = True}


pure func parse_msg(raw: String) -> Msg:
	match raw.trim():
		"+": Increment
		"-": Decrement
		"0": Reset
		"q": Quit
		_: Ignore


pure func update(model: Model, msg: Msg) -> Model:
	-- Every message returns the next model instead of mutating global state.
	match msg:
		Increment:
			{ model | value = model.value + 1 }
		Decrement:
			{ model | value = model.value - 1 }
		Reset:
			{ model | value = 0 }
		Quit:
			{ model | running = False }
		Ignore:
			model


pure func is_running(model: Model) -> Bool:
	model.running


pure func view(model: Model) -> String:
	status: String = if model.running:
		"running"
	else:
		"done"
	lines: List[String] = [
		"Counter TUI (" + status + ")",
		"value: " + model.value.to_string(),
		"+ increment | - decrement | 0 reset | q quit",
	]
	lines.join("\n")


func main(args: List[String]) -> Void:
	run_app(init(), parse_msg, update, view, is_running)

Try it

terminal
printf '+\n+\n-\nq\n' | blorp run todo-tui.brp