plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
plum-examples/safe_navigation.plum
| 43e5250 | 1 | import std/Option |
| 43e5250 | 2 | import std/Result |
| 43e5250 | 3 | import std/Bool |
| 43e5250 | 4 | import std/Number |
| 43e5250 | 5 | import std/Str |
| 43e5250 | 6 | # `obj?.field` / `obj?.method(args)` — Groovy/Kotlin-style safe navigation. |
| 43e5250 | 7 | # Reaches into a `Result`'s `Ok`/an `Option`'s `Some` without unwrapping it, |
| 43e5250 | 8 | # mapping the field/method access over the inner value and re-wrapping the |
| 43e5250 | 9 | # result; `Err`/`None` passes through untouched. Pure sugar for |
| 43e5250 | 10 | # `obj.map(|v| v.field)` — desugared entirely in the parser (see |
| 43e5250 | 11 | # `plum-core`'s `parseSafeAttribute`), so nothing past it needs to know this |
| 43e5250 | 12 | # syntax exists. |
| 43e5250 | 13 | |
| 43e5250 | 14 | enum Person = |
| 43e5250 | 15 | | Person(name: Str, age: Int) |
| 43e5250 | 16 | fun birthday(self) -> Int = |
| 43e5250 | 17 | self.age + 1 |
| 43e5250 | 18 | |
| 43e5250 | 19 | fun personName(p: Option[Person]) -> Option[Str] = |
| 43e5250 | 20 | p?.name |
| 43e5250 | 21 | |
| 43e5250 | 22 | fun personBirthday(p: Option[Person]) -> Option[Int] = |
| 43e5250 | 23 | p?.birthday() |
| 43e5250 | 24 | |
| 43e5250 | 25 | fun personNameResult(p: Result[Person, Str]) -> Result[Str, Str] = |
| 43e5250 | 26 | p?.name |
| 43e5250 | 27 | |
| 43e5250 | 28 | fun somePerson() -> Option[Person] = |
| 43e5250 | 29 | Some(Person(name: "Ann", age: 3)) |
| 43e5250 | 30 | |
| 43e5250 | 31 | fun noPerson() -> Option[Person] = |
| 43e5250 | 32 | None |
| 43e5250 | 33 | |
| 43e5250 | 34 | fun okPerson() -> Result[Person, Str] = |
| 43e5250 | 35 | Ok(Person(name: "Ann", age: 3)) |
| 43e5250 | 36 | |
| 43e5250 | 37 | fun errPerson() -> Result[Person, Str] = |
| 43e5250 | 38 | Err("missing") |
| 43e5250 | 39 | |
| 43e5250 | 40 | test "safe navigation maps a field access over Some, passes None through unchanged" |
| 43e5250 | 41 | a := personName(somePerson()) |
| 43e5250 | 42 | assert a.isSome() |
| 43e5250 | 43 | assert a.unwrap() == "Ann" |
| 43e5250 | 44 | b := personName(noPerson()) |
| 43e5250 | 45 | assert b.isNone() |
| 43e5250 | 46 | |
| 43e5250 | 47 | test "safe navigation maps a method call over Some" |
| 43e5250 | 48 | a := personBirthday(somePerson()) |
| 43e5250 | 49 | assert a.isSome() |
| 43e5250 | 50 | assert a.unwrap() == 4 |
| 43e5250 | 51 | |
| 43e5250 | 52 | test "safe navigation maps a field access over Ok, passes Err through unchanged" |
| 43e5250 | 53 | a := personNameResult(okPerson()) |
| 43e5250 | 54 | assert a.isOk() |
| 43e5250 | 55 | assert a.unwrap() == "Ann" |
| 43e5250 | 56 | b := personNameResult(errPerson()) |
| 43e5250 | 57 | assert b.isErr() |
| 43e5250 | 58 | assert b.unwrapErr() == "missing" |