plum

#treesitter#compiler#wasm

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