plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-examples/error_propagation.plum
| af1776b | 1 | import std/Result |
| af1776b | 2 | import std/Option |
| af1776b | 3 | import std/Bool |
| af1776b | 4 | import std/Number |
| af1776b | 5 | import std/Str |
| af1776b | 6 | import std/List |
| af1776b | 7 | |
| af1776b | 8 | # `expr?` unwraps a `Result`'s `Ok` or an `Option`'s `Some`, or exits the |
| af1776b | 9 | # enclosing function early with the `Err`/`None` value as-is otherwise — same |
| af1776b | 10 | # idea as Rust's `?`. The enclosing function's own return type must accept |
| af1776b | 11 | # whatever gets returned early; that isn't checked until codegen (see |
| af1776b | 12 | # `plum-checker`'s `inferExpr` on `Expr::Try`), so a mismatch there still |
| af1776b | 13 | # surfaces as a clear compile error, just a less precise one. |
| ec5336f | 14 | # |
| ec5336f | 15 | # `left ?: right` (elvis) is the plain-expression counterpart: same Ok/Some |
| ec5336f | 16 | # vs Err/None narrowing, but no early return — `right` is a fallback value, |
| ec5336f | 17 | # fully checked (unlike `?`) since there's no enclosing-return-type |
| ec5336f | 18 | # uncertainty to be permissive about. |
| af1776b | 19 | |
| af1776b | 20 | fun parsePositive(s: Str) -> Result[Int, Str] = |
| af1776b | 21 | n := parseInt(s)? |
| af1776b | 22 | if n < 0 |
| af1776b | 23 | return Err("negative") |
| af1776b | 24 | return Ok(n) |
| af1776b | 25 | |
| af1776b | 26 | fun sumTwo(a: Str, b: Str) -> Result[Int, Str] = |
| af1776b | 27 | x := parsePositive(a)? |
| af1776b | 28 | y := parsePositive(b)? |
| af1776b | 29 | return Ok(x + y) |
| af1776b | 30 | |
| af1776b | 31 | fun firstPositive(list: List[Int]) -> Option[Int] = |
| af1776b | 32 | match list.get(0) |
| af1776b | 33 | Some(v) => |
| af1776b | 34 | if v > 0 |
| af1776b | 35 | return Some(v) |
| af1776b | 36 | return None |
| af1776b | 37 | None => |
| af1776b | 38 | return None |
| af1776b | 39 | |
| af1776b | 40 | fun doubledFirstPositive(list: List[Int]) -> Option[Int] = |
| af1776b | 41 | a := firstPositive(list)? |
| af1776b | 42 | return Some(a * 2) |
| af1776b | 43 | |
| ec5336f | 44 | # `left ?: right` — same Ok/Some-vs-Err/None narrowing as `?`, but a plain |
| ec5336f | 45 | # expression (no early return): `right` is the fallback value, unified against |
| ec5336f | 46 | # the success field's type by the checker. |
| ec5336f | 47 | |
| ec5336f | 48 | fun sumOrZero(a: Str, b: Str) -> Int = |
| ec5336f | 49 | x := parsePositive(a) ?: 0 |
| ec5336f | 50 | y := parsePositive(b) ?: 0 |
| ec5336f | 51 | x + y |
| ec5336f | 52 | |
| ec5336f | 53 | fun firstOrDefault(list: List[Int], default: Int) -> Int = |
| ec5336f | 54 | list.get(0) ?: default |
| ec5336f | 55 | |
| af1776b | 56 | test "try operator unwraps Ok and propagates the value through two calls" |
| af1776b | 57 | r := sumTwo("2", "3") |
| af1776b | 58 | assert r.isOk() |
| af1776b | 59 | assert r.unwrap() == 5 |
| af1776b | 60 | |
| af1776b | 61 | test "try operator exits early with Err, skipping the rest of the function" |
| af1776b | 62 | r := sumTwo("2", "-3") |
| af1776b | 63 | assert r.isErr() |
| af1776b | 64 | assert r.unwrapErr() == "negative" |
| af1776b | 65 | |
| af1776b | 66 | test "try operator unwraps Some and propagates the value" |
| af1776b | 67 | l := List(5) |
| af1776b | 68 | r := doubledFirstPositive(l) |
| af1776b | 69 | assert r.isSome() |
| af1776b | 70 | assert r.unwrap() == 10 |
| af1776b | 71 | |
| af1776b | 72 | test "try operator exits early with None, skipping the rest of the function" |
| af1776b | 73 | l := List[Int]() |
| af1776b | 74 | r := doubledFirstPositive(l) |
| af1776b | 75 | assert r.isNone() |
| ec5336f | 76 | |
| ec5336f | 77 | test "elvis operator unwraps Ok and falls back to 0 on Err" |
| ec5336f | 78 | assert sumOrZero("2", "3") == 5 |
| ec5336f | 79 | assert sumOrZero("2", "-3") == 2 |
| ec5336f | 80 | |
| ec5336f | 81 | test "elvis operator unwraps Some and falls back to the given default on None" |
| ec5336f | 82 | assert firstOrDefault(List(5), 99) == 5 |
| ec5336f | 83 | assert firstOrDefault(List[Int](), 99) == 99 |