plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
09d918f
— Peter John
2026-10-25T17:27:30+05:30
docs(examples): multi-branch match error handling in error_propagation
plum-examples/error_propagation.plum
CHANGED
|
@@ -6,8 +6,8 @@ import std/List
|
|
|
6
6
|
|
|
7
7
|
# `Try[T, E]` is either `Ok(T)` or `Err(E)`. `E` is normally a tag union such
|
|
8
8
|
# as `[NegativeNum, BadNumStr]`: a set of bare tags (optionally carrying a
|
|
9
|
-
# payload, e.g. `InvalidJson(Str)`) that needs no declaration. There is no
|
|
9
|
+
# payload, e.g. `InvalidJson(Str)`) that needs no declaration. There is no `Option`:
|
|
10
|
-
#
|
|
10
|
+
# a missing value is just an error tag like `[OutOfBounds]`.
|
|
11
11
|
#
|
|
12
12
|
# `expr?` unwraps an `Ok`, or exits the enclosing function early with the
|
|
13
13
|
# `Err` value. The error unions of different calls merge automatically into
|
|
@@ -62,6 +62,37 @@ fun mustBePositive(n: Int) -> Int =
|
|
|
62
62
|
crash "n must not be negative"
|
|
63
63
|
n
|
|
64
64
|
|
|
65
|
+
# Handling errors with `match`: one arm per tag, payloads bind like any
|
|
66
|
+
# variant field, and a match without `_` must cover every tag in the union.
|
|
67
|
+
fun parseAge(s: Str) -> Try[Int, [BadNumStr, Negative, TooOld(Int)]] =
|
|
68
|
+
n := parseInt(s)?
|
|
69
|
+
if n < 0
|
|
70
|
+
return Err(Negative)
|
|
71
|
+
if n > 150
|
|
72
|
+
return Err(TooOld(n))
|
|
73
|
+
return Ok(n)
|
|
74
|
+
|
|
75
|
+
fun describeAge(s: Str) -> Str =
|
|
76
|
+
match parseAge(s)
|
|
77
|
+
Ok(n) => "age {n}"
|
|
78
|
+
Err(BadNumStr) => "not a number"
|
|
79
|
+
Err(Negative) => "can't be negative"
|
|
80
|
+
Err(TooOld(n)) => "{n} is too old"
|
|
81
|
+
|
|
82
|
+
# A bare tag union can be matched directly too.
|
|
83
|
+
fun isRetryable(e: [Timeout, Refused, BadRequest(Str)]) -> Bool =
|
|
84
|
+
match e
|
|
85
|
+
Timeout => True
|
|
86
|
+
Refused => True
|
|
87
|
+
BadRequest(_) => False
|
|
88
|
+
|
|
89
|
+
# `_` catches whatever the earlier arms didn't.
|
|
90
|
+
fun ageOrDefault(s: Str) -> Int =
|
|
91
|
+
match parseAge(s)
|
|
92
|
+
Ok(n) => n
|
|
93
|
+
Err(TooOld(_)) => 150
|
|
94
|
+
_ => 0
|
|
95
|
+
|
|
65
96
|
test "try operator unwraps Ok and propagates the value through two calls"
|
|
66
97
|
r := sumTwo("2", "3")
|
|
67
98
|
assert r.isOk()
|
|
@@ -117,3 +148,19 @@ test "default operator falls back to the given default on an empty list"
|
|
|
117
148
|
|
|
118
149
|
test "crash is not reached on the happy path"
|
|
119
150
|
assert mustBePositive(3) == 3
|
|
151
|
+
|
|
152
|
+
test "match handles each error tag in its own arm"
|
|
153
|
+
assert describeAge("42") == "age 42"
|
|
154
|
+
assert describeAge("abc") == "not a number"
|
|
155
|
+
assert describeAge("-3") == "can't be negative"
|
|
156
|
+
assert describeAge("200") == "200 is too old"
|
|
157
|
+
|
|
158
|
+
test "match on a bare tag union"
|
|
159
|
+
assert isRetryable(Timeout)
|
|
160
|
+
assert isRetryable(Refused)
|
|
161
|
+
assert !isRetryable(BadRequest("missing id"))
|
|
162
|
+
|
|
163
|
+
test "match falls back to a wildcard arm"
|
|
164
|
+
assert ageOrDefault("30") == 30
|
|
165
|
+
assert ageOrDefault("999") == 150
|
|
166
|
+
assert ageOrDefault("x") == 0
|