Migrating to Rad v0.9¶
Version 0.9 introduces changes to error handling operators and renames a built-in function. This guide covers what changed and how to update your scripts.
Breaking Change: get_stash_dir Renamed¶
What Changed¶
get_stash_dir was renamed to get_stash_path to better reflect its behavior - it returns a path (often to a file), not necessarily a directory.
Old Syntax (No Longer Works)¶
path = get_stash_dir()
path = get_stash_dir("data/config.json")
New Syntax¶
path = get_stash_path()
path = get_stash_path("data/config.json")
Migration Steps¶
- Find all uses of
get_stash_dirin your scripts - Replace with
get_stash_path- the signature and behavior are identical
Breaking Change: ?? Now Fires on Null¶
What Changed¶
?? is now a true null-coalescing operator. It fires when the left side is null or an error, where previously it only fired on errors.
Before (v0.8)¶
m = {"key": null}
print(m["key"] ?? "fallback") // printed: null
print(null ?? "default") // printed: null
After (v0.9)¶
m = {"key": null}
print(m["key"] ?? "fallback") // prints: fallback
print(null ?? "default") // prints: default
Why¶
This aligns ?? with developer expectations from JS/Kotlin/Swift where ?? is a null-coalescing operator. The old error-only behavior is now available via the new catch operator (see below).
Migration¶
If you relied on null passing through ??, use catch instead:
// If you want error-only catching (old behavior), use catch:
result = maybe_error_value catch "default"
New: catch Operator¶
The catch operator provides the old ?? error-only behavior as an inline expression. It catches errors but lets null values pass through.
count = parse_int(input_str) catch 0
data = parse_json(raw) catch {}
This is distinct from the catch: block syntax. The operator form is an inline expression; catch: is a block attached to statements.
Breaking Change: Strict + Concatenation¶
What Changed¶
The + operator no longer implicitly converts int, float, or bool to strings. Both operands must be the same type (with the exception that errors behave like strings for concatenation).
Before (v0.8)¶
print("count: " + 5) // printed: count: 5
print("pi: " + 3.14) // printed: pi: 3.14
print("flag: " + true) // printed: flag: true
err = error("oops")
print(err + 123) // printed: oops123
After (v0.9)¶
print("count: " + 5) // error: RAD30002
print("pi: " + 3.14) // error: RAD30002
print("flag: " + true) // error: RAD30002
err = error("oops")
print(err + 123) // error: RAD30002
Why¶
Implicit coercion was asymmetric ("hi" + 5 worked, but 5 + "hi" errored) and used a different conversion path from string interpolation. Making + strict catches type bugs at the point of error rather than silently producing unexpected strings.
Migration¶
Use interpolation (preferred) or str() for explicit conversion:
// Interpolation - handles any type, recommended
print("count: {5}")
print("pi: {3.14}")
// Explicit conversion with str()
print("count: " + str(5))
print("pi: " + str(3.14))
Note: errors still behave like strings for concatenation ("s" + error("e"), error("e") + "s", and error("a") + error("b") all work).
Error Messages¶
If you run a script that still uses get_stash_dir, you'll see a helpful error:
error: Cannot invoke unknown function: get_stash_dir
hint: get_stash_dir was renamed to get_stash_path.
See: https://amterp.github.io/rad/migrations/v0.9/