plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-examples/error_propagation.plum
import std/Result
import std/Option
import std/Bool
import std/Number
import std/Str
import std/List
# `expr?` unwraps a `Result`'s `Ok` or an `Option`'s `Some`, or exits the
# enclosing function early with the `Err`/`None` value as-is otherwise — same
# idea as Rust's `?`. The enclosing function's own return type must accept
# whatever gets returned early; that isn't checked until codegen (see
# `plum-checker`'s `inferExpr` on `Expr::Try`), so a mismatch there still
# surfaces as a clear compile error, just a less precise one.
#
# `left ?: right` (elvis) is the plain-expression counterpart: same Ok/Some
# vs Err/None narrowing, but no early return — `right` is a fallback value,
# fully checked (unlike `?`) since there's no enclosing-return-type
# uncertainty to be permissive about.
fun parsePositive(s: Str) -> Result[Int, Str] =
n := parseInt(s)?
if n < 0
return Err("negative")
return Ok(n)
fun sumTwo(a: Str, b: Str) -> Result[Int, Str] =
x := parsePositive(a)?
y := parsePositive(b)?
return Ok(x + y)
fun firstPositive(list: List[Int]) -> Option[Int] =
match list.get(0)
Some(v) =>
if v > 0
return Some(v)
return None
None =>
return None
fun doubledFirstPositive(list: List[Int]) -> Option[Int] =
a := firstPositive(list)?
return Some(a * 2)
# `left ?: right` — same Ok/Some-vs-Err/None narrowing as `?`, but a plain
# expression (no early return): `right` is the fallback value, unified against
# the success field's type by the checker.
fun sumOrZero(a: Str, b: Str) -> Int =
x := parsePositive(a) ?: 0
y := parsePositive(b) ?: 0
x + y
fun firstOrDefault(list: List[Int], default: Int) -> Int =
list.get(0) ?: default
test "try operator unwraps Ok and propagates the value through two calls"
r := sumTwo("2", "3")
assert r.isOk()
assert r.unwrap() == 5
test "try operator exits early with Err, skipping the rest of the function"
r := sumTwo("2", "-3")
assert r.isErr()
assert r.unwrapErr() == "negative"
test "try operator unwraps Some and propagates the value"
l := List(5)
r := doubledFirstPositive(l)
assert r.isSome()
assert r.unwrap() == 10
test "try operator exits early with None, skipping the rest of the function"
l := List[Int]()
r := doubledFirstPositive(l)
assert r.isNone()
test "elvis operator unwraps Ok and falls back to 0 on Err"
assert sumOrZero("2", "3") == 5
assert sumOrZero("2", "-3") == 2
test "elvis operator unwraps Some and falls back to the given default on None"
assert firstOrDefault(List(5), 99) == 5
assert firstOrDefault(List[Int](), 99) == 99