Documentation / Core packages / Core IO

Core IO

io provides console output and explicit input polling. The prelude exposes its namespace without a dependency clause, while templates handle value formatting before text crosses the output boundary.

Choose the explanation level: Console and input

Console and input

io gives your program console output and input polling. The prelude makes io available without a dependency clause, so you can start writing output right away.

Writing to the console

Console helpers take one string and write it to the output surface.

io.print("loading...")
io.line("Hello")
io.debug("debug details")
io.warn("warning")
io.error("error")

io.line adds a trailing newline. io.print does not.

Formatting values

Console helpers accept strings only. Use a template to interpolate values into text before output.

name = "Priya"
io.line([: Hello, [name]])

count = 3
io.line([: count = [count]])

Reading input

Input polling lives under io.input.*. Create an input handle, poll it each frame, then close it when done.

input ~= io.input.new() catch:
    assert(false, "input backend unavailable")
;

io.input.update(~input)

if io.input.key_pressed(input, "d"):
    io.line("pressed d")
;

io.input.close(~input)

io.input.new() can fail when the backend does not support input. update and close need mutable access, so they use ~. Polling reads such as key_pressed use shared access.

Pointer input

Pointer helpers report button state and coordinates.

if io.input.pointer_down(input, "left"):
    x = io.input.pointer_x(input)
    y = io.input.pointer_y(input)

    io.line([: pointer [x], [y]])
;

Buttons use "left", "middle" and "right". Coordinates use Float values. For the full contract including backend behaviour, teardown rules and deferred surfaces, see Core IO advanced.

@core/io owns console output and input polling. The prelude injects the bare io namespace as a compile-time alias to @core/io, so normal source files can call io.line(...) without an explicit dependency clause. An explicit @core/io clause binds the same io namespace. An alias such as @core/io as output creates a file-local namespace name.

@core/io belongs to the binding-backed Core package family. It exposes free functions and one opaque type. It does not expose source-defined receiver methods. See Package origins and backing for origin and backing rules.

Console helpers

Console helpers write string content to the backend output surface. Each helper takes one String value and returns nothing.

Helper

Output lane

io.print(text)

plain output

io.line(text)

output with a trailing newline

io.debug(text)

debug-level output

io.warn(text)

warning-level output

io.error(text)

error-level output

The output boundary accepts String values only. Templates produce String values, so template interpolation provides the way to format non-string content for output.

io.print("loading...")
io.line("Hello")
io.debug("debug details")
io.warn("warning")
io.error("error")

name = "Priya"
io.line([: Hello, [name]])

count = 3
io.line([: count = [count]])

Passing a non-string value directly to a console helper fails at the output boundary. Interpolate the value into a template first.

Input polling

Input polling lives under io.input.*. The handle type io.input.Input stays opaque. Pass it to the input functions. Do not construct it with struct syntax or inspect its fields.

io.input.new() returns Input, Error! because backend input support can be unavailable. update and close require mutable access through ~. Polling reads use shared access.

input ~= io.input.new() catch:
    assert(false, "input backend unavailable")
;

io.input.update(~input)

if io.input.key_pressed(input, "d"):
    io.line("pressed d")
;

if io.input.pointer_down(input, "left"):
    x = io.input.pointer_x(input)
    y = io.input.pointer_y(input)

    io.line([: pointer [x], [y]])
;

last_key = io.input.last_key_pressed(input)

if last_key is |key|:
    io.line([: last key: [key]])
;

io.input.close(~input)

Keyboard helpers

Keyboard helpers track down, pressed, released and last-edge states.

Helper

Signature

Access

key_down(input, key)

Input, String -> Bool

shared

key_pressed(input, key)

Input, String -> Bool

shared

key_released(input, key)

Input, String -> Bool

shared

last_key_pressed(input)

Input -> String?

shared

last_key_released(input)

Input -> String?

shared

Single alphabetic key strings normalise to lowercase. Space uses "Space". Special keys use logical names such as "ArrowLeft", "Enter", "Escape", "Backspace" and "Shift".

Pointer helpers

Pointer helpers track button state and position.

Helper

Signature

Access

pointer_down(input, button)

Input, String -> Bool

shared

pointer_pressed(input, button)

Input, String -> Bool

shared

pointer_released(input, button)

Input, String -> Bool

shared

pointer_x(input)

Input -> Float

shared

pointer_y(input)

Input -> Float

shared

last_pointer_pressed(input)

Input -> String?

shared

last_pointer_released(input)

Input -> String?

shared

Pointer buttons use "left", "middle" and "right". Pointer coordinates use Float values.

Teardown

io.input.close(~input) releases the input handle. Call it when the input handle no longer needs polling. The handle requires mutable access for close because teardown mutates the underlying host state.

Backend behaviour

HTML-JS maps console helpers to the browser console and input helpers to window and document-level keyboard and pointer polling. HTML-Wasm rejects reachable Core IO calls before lowering until it gains equivalent support.

Browser input events arrive through the host event loop. A synchronous infinite Moth loop can stop new input from arriving. Yield control back to the event loop between polling cycles.

Unsupported source forms

  • Do not construct Input with struct syntax.
  • Do not inspect Input fields directly.
  • Do not pass non-string values to console helpers.
  • Do not pass the io namespace record itself as a runtime value.
  • Direct symbol-path clauses such as @core/io/line face rejection. Use direct selections or a namespace clause.

Deferred IO surfaces

Deferred IO domains include filesystem and path IO, fetch and network IO, timers, sleep, intervals, frame and tick APIs, targeted DOM and canvas input sources, physical key-code APIs, typed key and button choices, text entry and IME or composition, touch gestures, gamepads, drag and drop, clipboard, wheel scrolling, file picker APIs, full ordered event queues, pressed and released collections, relative or DPI-scaled pointer coordinates, configurable default suppression and non-JS lowerings. See progress matrix for current target availability.

Related concepts