Running a command is an effect.
A value is.
A string, list, record, variant, or block; immutable. Retrieve it; it cannot act by itself.
A command does.
A computation that may read, emit bytes, return, or fail. Force it; the surrounding scope supplies its interpretation.
Effect handlers
Both programs below test a deployment without touching the network.
Bash makes curl a global shell function and must remember
to remove it. ral installs another interpretation of
curl for one dynamic extent. At the closing brace, the
operating system is the interpretation again.
# Mock: shadow curl for the test. This override is global from here on.
curl() { echo '{"status":"ok","rev":"abc123"}'; }
deploy prod.example.com
deploy staging.example.com
unset -f curl # easy to forget!
within [
handlers: [
curl: { |args|
echo #'{"status": "ok", "rev": "abc123"}'#
}
]
] {
deploy 'prod.example.com'
deploy 'staging.example.com'
}
Values, not words
Most shells make safe argument passing a matter of quoting
discipline. ral removes word splitting from the language.
$ARGS is a list of strings, and for
receives those exact values: carol smith cannot fracture
into two names.
for name in "$@"; do
echo "hello, $name"
done
let names = if !{is-empty $ARGS} {
return [world]
} else {
return $ARGS
}
for $names { |n| echo "hello, $n" }
grant wraps a block with narrower authority over
commands, filesystem paths, and the network. Every function and
command called inside inherits that boundary. A nested
grant intersects with its caller; it can take
authority away, never give it back.
grant [
exec: [git: ['status']],
fs: [read: ['cwd:'], write: []],
net: false,
] {
git status
}
This is enforcement, not metadata.
ral checks its own redirects and dispatch in-process;
external and bundled commands enter Seatbelt on macOS,
bwrap on Linux, or an AppContainer on Windows. If a
requested restriction cannot be enforced, the command fails
closed.
The difference
$file cannot split into two arguments.false cannot masquerade as a failed command.grant cannot widen its caller's authority.> cannot leave a regular file half-written.Two pipes, one bar
cat log | grep ERROR remains a genuine Unix byte
pipeline. range 1 21 | filter $even | sum is typed
function composition. Codecs such as from-json name the
exact point where bytes become values; a mismatched edge is rejected
before any process starts.
Failure travels on a different axis. | moves data;
? chooses a fallback after failure; if
branches on a Bool. Falsehood and failure never trade
places.
Paths with spaces. grep returning 1. Atomic file
updates. Quoted CSV. Cancelled races. Scoped mocks.
Network-denied builds. Each example names the Bash footgun, then
places the Bash and ral programs side by side.
exarch executes every model action as a ral program
under a grant installed by the host. The agent may use that
authority but cannot widen it; the execution tree is the audit.
exarch does not bolt a sandbox around ral — it uses the
shell's own semantics.
curl -fsSL https://lambdabetaeta.github.io/ral/scripts/install.sh | sh
Batteries included. The
“with utils” build folds a curated set of
uutils coreutils — the
cross-platform Rust reimplementation of GNU coreutils —
into ral (ls, cat, cp,
mv, etc.). diffutils,
grep, and ripgrep are bundled too.
| Platform | Binary | With utils | Installer |
|---|---|---|---|
| macOS arm64 | ral-macos-arm64 | ral-macos-arm64-allutils | — |
| Linux x86_64 | ral-linux-x86_64 | ral-linux-x86_64-allutils | — |
| Linux arm64 | ral-linux-arm64 | ral-linux-arm64-allutils | — |
| Windows | — | — | ral-windows.msi |
Not POSIX, on purpose. POSIX compatibility requires
word splitting, $IFS, implicit glob expansion, and
context-dependent quoting. Those are precisely the mechanisms ral
refuses. Bash scripts do not run unchanged.
The value–command split comes from
call-by-push-value.
Treating an external command as an operation whose interpretation is
supplied separately gives ral algebraic effects.
The specification says what ral does.
The rationale says why.
Funded by the Advanced Research + Invention Agency (ARIA).