plum

#treesitter#compiler#wasm

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
Files changed (1) hide show
  1. README.md +53 -0
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