I experimented with roc for scripting and want to share my thoughts and experience.
I replaced a 10 line posix shell script and a more complex janet script with roc.
original posix script:
#!/bin/sh
tmpTYPSTFile="/tmp/typst2pdf$$.typ"
tmpPDFFile="/tmp/typst2pdf$$.pdf"
trap 'rm -f -- "$tmpTYPSTFile" "$tmpPDFFile"' EXIT TERM INT QUIT
[ -t 0 ] && { printf "%s: stdin not provided\n" "$0" >&2 ; exit 1; }
cat > "$tmpTYPSTFile"
typst compile --pdf-standard 2.0 --format=pdf "$tmpTYPSTFile" "$tmpPDFFile"
cat "$tmpPDFFile"
same script in roc
#!/usr/bin/env roc
app [main!] {
pf: platform "https://codeberg.org/removewingman/restricted-roc/releases/download/v0.2.0/DP5N5ev2nXZ1XSKWHPQZnjSLz5CmfCxYm3QAfrrMxoWR.tar.zst",
}
import pf.Stdin
import pf.Stdout
import pf.Stderr
import pf.Script
import pf.File
import pf.Process
main! = |args| {
Script.stdin_provided_else_exit!({ message: "${args.get(0)?}: stdin not provided" })?
input_file = File.create_bytes_tmp!({ name: "typst2pdf", extension: "typ", bytes_content: Stdin.bytes!()? })?
output_file = File.tmp_name!({ name: "typst2pdf", extension: "pdf" })?
match Process.run!({ bin: "typst", args: "compile --pdf-standard 2.0 --format=pdf ${input_file} ${output_file}" })?.success_and_stderr_empty({ error_message: "typst error" }) {
Ok(_) => {
output = File.content_bytes!({ absolute_path: output_file })?
Stdout.write_bytes!(output)
}
Err((result, message)) => {
result.exit_write_to_stderr!(message)
}
}
}
!, ?, dbg and crashtrap functionality! and try? functions, e.g. for_each_try!import pf.[Stdin, Stdout] (anything so 10 lines are not wasted on imports)structural records to have defaults
tmp_name! : { name: Str, extension: Str, automatically_delete: Bool ?? true } => Try({}, [FileCreateErr(Str)])
(throws compile error)roc works on more unix systems e.g. FreeBSD/OpenBSD
Let me know what you think :)
Thanks for sharing your experience @removewingman :)
shadowing local variables bindings inside a function scope
Are you aware of the var keyword?
structural records to have defaults
We used to have these but it resulted in a lot of problems with type checking, so it was removed.
Looks like you made PR#11096 for for_each_try! but you closed it :sweat_smile:
I am aware of the var keyword, meant something like this:
a = "b"
a = a.trim()
Yeah, I know. If I have some better hardware and some local AI Agent will try to contribute to roc Builtins.
Having to test everthing in CI is annoying.
Last updated: Nov 04 2026 at 18:54 UTC