plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-std/Result.plum
| fa31ba4 | 1 | module std |
| b7071c9 | 2 | import std/Option |
| b7071c9 | 3 | import std/Str |
| 73b5e55 | 4 | import std/Bool |
| ca5fd6f | 5 | import std/Number |
| fa31ba4 | 6 | |
| a271f34 | 7 | # Result[T, E] represents either success (`Ok`, carrying a `T`) or failure |
| a271f34 | 8 | # (`Err`, carrying an `E`) — used throughout std for fallible operations |
| a271f34 | 9 | # (`Bool.parse`, `Int.fromStr`, `Float.fromStr`). |
| a271f34 | 10 | # |
| 2ec05c8 | 11 | # `andThen` (which would need `cb`'s return type's `T` to be found NESTED |
| 2ec05c8 | 12 | # inside another `Result[U, E]`, not `cb`'s own bare return type) is |
| 2ec05c8 | 13 | # intentionally not implemented here — same method-level-generics gap |
| 2ec05c8 | 14 | # documented on `Option.andThen` in `libs/std/option.plum`. `map`/`mapErr` |
| 2ec05c8 | 15 | # don't have that problem (`cb`'s return type IS directly the new generic |
| 2ec05c8 | 16 | # param, same shape as `List.map`), so those ARE implemented below. |
| 287b97c | 17 | enum Result[T, E] = |
| 287b97c | 18 | | Ok(T) |
| 287b97c | 19 | | Err(E) |
| 5de508e | 20 | |
| 58b01fc | 21 | fun isOk(self) -> Bool = |
| 0fe3528 | 22 | match self |
| 3a2119e | 23 | Ok(_) => True |
| 3a2119e | 24 | Err(_) => False |
| a271f34 | 25 | |
| a271f34 | 26 | fun isErr(self) -> Bool = |
| a271f34 | 27 | match self |
| 3a2119e | 28 | Ok(_) => False |
| 3a2119e | 29 | Err(_) => True |
| a271f34 | 30 | |
| a271f34 | 31 | # Returns the wrapped success value, or traps if `self` is `Err`. |
| a271f34 | 32 | fun unwrap(self) -> T = |
| a271f34 | 33 | match self |
| a271f34 | 34 | Ok(v) => |
| a271f34 | 35 | return v |
| a271f34 | 36 | Err(_) => |
| a271f34 | 37 | todo |
| a271f34 | 38 | |
| a271f34 | 39 | # Returns the wrapped error value, or traps if `self` is `Ok`. |
| a271f34 | 40 | fun unwrapErr(self) -> E = |
| a271f34 | 41 | match self |
| a271f34 | 42 | Ok(_) => |
| a271f34 | 43 | todo |
| a271f34 | 44 | Err(e) => |
| a271f34 | 45 | return e |
| a271f34 | 46 | |
| a271f34 | 47 | # Returns the wrapped success value, or traps if `self` is `Err`. `msg` |
| a271f34 | 48 | # documents the expectation at the call site (matching Rust's |
| a271f34 | 49 | # `Result::expect`) but isn't surfaced anywhere at runtime — see the |
| a271f34 | 50 | # identical note on `Option.expect`. |
| a271f34 | 51 | fun expect(self, msg: Str) -> T = |
| a271f34 | 52 | match self |
| a271f34 | 53 | Ok(v) => |
| a271f34 | 54 | return v |
| a271f34 | 55 | Err(_) => |
| a271f34 | 56 | todo |
| a271f34 | 57 | |
| a271f34 | 58 | fun expectErr(self, msg: Str) -> E = |
| a271f34 | 59 | match self |
| a271f34 | 60 | Ok(_) => |
| a271f34 | 61 | todo |
| a271f34 | 62 | Err(e) => |
| a271f34 | 63 | return e |
| a271f34 | 64 | |
| a271f34 | 65 | fun unwrapOr(self, default: T) -> T = |
| a271f34 | 66 | match self |
| a271f34 | 67 | Ok(v) => |
| a271f34 | 68 | return v |
| a271f34 | 69 | Err(_) => |
| a271f34 | 70 | return default |
| a271f34 | 71 | |
| a271f34 | 72 | # Like `unwrapOr`, but the fallback is computed lazily from the error via |
| a271f34 | 73 | # `cb` (only when `self` is `Err`). |
| a271f34 | 74 | fun unwrapOrElse(self, cb: fn(E) -> T) -> T = |
| a271f34 | 75 | match self |
| a271f34 | 76 | Ok(v) => |
| a271f34 | 77 | return v |
| a271f34 | 78 | Err(e) => |
| a271f34 | 79 | return cb(e) |
| a271f34 | 80 | |
| a271f34 | 81 | # Discards the error, keeping only the success value (if any). |
| a271f34 | 82 | fun ok(self) -> Option[T] = |
| a271f34 | 83 | match self |
| a271f34 | 84 | Ok(v) => |
| a271f34 | 85 | return Some(v) |
| a271f34 | 86 | Err(_) => |
| a271f34 | 87 | return None |
| a271f34 | 88 | |
| a271f34 | 89 | # Discards the success value, keeping only the error (if any). |
| a271f34 | 90 | fun err(self) -> Option[E] = |
| a271f34 | 91 | match self |
| a271f34 | 92 | Ok(_) => |
| a271f34 | 93 | return None |
| a271f34 | 94 | Err(e) => |
| a271f34 | 95 | return Some(e) |
| a271f34 | 96 | |
| 2ec05c8 | 97 | # Transforms the success value with `cb`, leaving `Err` untouched. `U` is |
| 2ec05c8 | 98 | # `map`'s OWN generic param, separate from `Result[T, E]`'s own `T`/`E` — |
| 2ec05c8 | 99 | # resolved per CALL SITE, same mechanism as `List.map`/`Option.map`. |
| 2ec05c8 | 100 | fun map(self, cb: fn(T) -> U) -> Result[U, E] = |
| 2ec05c8 | 101 | match self |
| 2ec05c8 | 102 | Ok(v) => |
| 2ec05c8 | 103 | return Ok(cb(v)) |
| 2ec05c8 | 104 | Err(e) => |
| 2ec05c8 | 105 | return Err(e) |
| 2ec05c8 | 106 | |
| 2ec05c8 | 107 | # Transforms the error value with `cb`, leaving `Ok` untouched. `F` is |
| 2ec05c8 | 108 | # `mapErr`'s OWN generic param, separate from `Result[T, E]`'s own `T`/`E`. |
| 2ec05c8 | 109 | fun mapErr(self, cb: fn(E) -> F) -> Result[T, F] = |
| 2ec05c8 | 110 | match self |
| 2ec05c8 | 111 | Ok(v) => |
| 2ec05c8 | 112 | return Ok(v) |
| 2ec05c8 | 113 | Err(e) => |
| 2ec05c8 | 114 | return Err(cb(e)) |
| 2ec05c8 | 115 | |
| a271f34 | 116 | # No implicit `toStr(self) -> Str` — see the identical note on |
| a271f34 | 117 | # `Option.toOptionStr` in `libs/std/option.plum`: a method that assumes |
| bf629a2 | 118 | # `T`/`E: ToStr` gets compiled for EVERY specialization of `Result` |
| a271f34 | 119 | # anywhere in the program (there's no lazy/on-demand method compilation), |
| a271f34 | 120 | # so an implicit bound like this can break an unrelated specialization |
| a271f34 | 121 | # whose payload doesn't support it. Explicit stringifier callbacks instead. |
| a271f34 | 122 | fun toResultStr(self, okToStr: fn(T) -> Str, errToStr: fn(E) -> Str) -> Str = |
| a271f34 | 123 | match self |
| a271f34 | 124 | Ok(v) => |
| a271f34 | 125 | return "Ok({okToStr(v)})" |
| a271f34 | 126 | Err(e) => |
| a271f34 | 127 | return "Err({errToStr(e)})" |
| 2ec05c8 | 128 | |
| 2ec05c8 | 129 | fun makeOkForResultTest() -> Result[Int, Str] = |
| 2ec05c8 | 130 | return Ok(5) |
| 2ec05c8 | 131 | |
| 2ec05c8 | 132 | fun makeErrForResultTest() -> Result[Int, Str] = |
| 2ec05c8 | 133 | return Err("bad") |
| 2ec05c8 | 134 | |
| 2ec05c8 | 135 | test "map transforms an Ok value and leaves Err untouched" |
| a9a0147 | 136 | assert makeOkForResultTest().map(|v| v * 2).unwrap() == 10 |
| a9a0147 | 137 | assert makeErrForResultTest().map(|v| v * 2).isErr() == True |
| 2ec05c8 | 138 | |
| 2ec05c8 | 139 | test "mapErr transforms an Err value and leaves Ok untouched" |
| a9a0147 | 140 | assert makeErrForResultTest().mapErr(|e| e + "!").unwrapErr() == "bad!" |
| a9a0147 | 141 | assert makeOkForResultTest().mapErr(|e| e + "!").unwrap() == 5 |