plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
b5abd9a
— Peter John
2026-10-25T17:31:17+05:30
docs(readme): handling errors with match, crashing and safe navigation sections
README.md
CHANGED
|
@@ -384,8 +384,61 @@ fun sumOrZero(a: Str, b: Str) -> Int =
|
|
|
384
384
|
x + y
|
|
385
385
|
```
|
|
386
386
|
|
|
387
|
+
### Handling errors with `match`
|
|
388
|
+
|
|
389
|
+
To react to each failure differently, `match` on the `Try`. Each error tag gets its own `Err(...)` arm, and a tag's payload binds like any variant field:
|
|
390
|
+
|
|
391
|
+
```plum
|
|
392
|
+
fun parseAge(s: Str) -> Try[Int, [BadNumStr, Negative, TooOld(Int)]] =
|
|
393
|
+
n := parseInt(s)?
|
|
394
|
+
if n < 0
|
|
395
|
+
return Err(Negative)
|
|
396
|
+
if n > 150
|
|
397
|
+
return Err(TooOld(n))
|
|
398
|
+
return Ok(n)
|
|
399
|
+
|
|
400
|
+
fun describeAge(s: Str) -> Str =
|
|
401
|
+
match parseAge(s)
|
|
402
|
+
Ok(n) => "age {n}"
|
|
403
|
+
Err(BadNumStr) => "not a number"
|
|
404
|
+
Err(Negative) => "can't be negative"
|
|
405
|
+
Err(TooOld(n)) => "{n} is too old"
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The union is closed, so a `match` without a catch-all has to handle every tag. Leaving one out is a compile error, which means adding a tag to a function's signature points you at every place that has to deal with it:
|
|
409
|
+
|
|
410
|
+
```
|
|
411
|
+
non-exhaustive match on [BadNumStr,Negative,TooOld(Int)]: missing TooOld
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
`_` (or `Err(_)` for "any error") handles everything the earlier arms didn't:
|
|
415
|
+
|
|
416
|
+
```plum
|
|
417
|
+
fun ageOrDefault(s: Str) -> Int =
|
|
418
|
+
match parseAge(s)
|
|
419
|
+
Ok(n) => n
|
|
420
|
+
Err(TooOld(_)) => 150
|
|
421
|
+
_ => 0
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
A tag union is an ordinary type, so you can also take one as a parameter and match on it directly:
|
|
425
|
+
|
|
426
|
+
```plum
|
|
427
|
+
fun isRetryable(e: [Timeout, Refused, BadRequest(Str)]) -> Bool =
|
|
428
|
+
match e
|
|
429
|
+
Timeout => True
|
|
430
|
+
Refused => True
|
|
431
|
+
BadRequest(_) => False
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Tags without a payload also support `==` (`e == Timeout`). A payload tag has to be matched.
|
|
435
|
+
|
|
436
|
+
### Crashing
|
|
437
|
+
|
|
387
438
|
`crash "msg"` aborts the program with a message. Use it for bugs (a broken invariant), not for expected failures; `unwrap()` on an `Err` crashes the same way.
|
|
388
439
|
|
|
440
|
+
### Safe navigation
|
|
441
|
+
|
|
389
442
|
`obj?.field` / `obj?.method(args)` (safe navigation, Groovy/Kotlin-style) reaches into a `Try` value without unwrapping it — `Ok` maps the field or method access over the inner value and re-wraps it, `Err` passes through untouched:
|
|
390
443
|
|
|
391
444
|
```plum
|