or-else is Java's, not Rust's, and the None it is tested against is reached

Four corrections to the prose and one to the test, none to the design.

The provenance line said "the name is Rust's or_else, the behaviour is
Rust's unwrap_or", which conflates two functions that differ in both
eagerness and return type — Rust's or_else takes a closure and answers
another Option.  Java's Optional.orElse is the exact match, and its lazy
sibling orElseGet is the one already declined a paragraph above.

The helper reaching or-else's None branch at an owning type asserted a
refusal nobody had run.  Compiled, it is "nothing here says what None is
an Option of — annotate the function's return type or the binding", so
the comment quotes that and the helper is a return type and nothing
else: its other branch was never called, in a program whose header says
every line is a claim.

read-file's comment claimed both restarts arrive unchanged and the test
runs use-value.  Narrowed to the mechanism (nothing here establishes a
handler) plus the half that is actually executed.

And edn-read.flan now says what becomes of its defn wrapper when the
computed-initialiser work lands, since that is the only thing keeping
the motivating line from being written as the defvar.
This commit is contained in:
Joseph Ferano 2026-09-19 04:30:57 +07:00
parent 7a0b2b7803
commit f35af50f51
4 changed files with 23 additions and 21 deletions

View File

@ -385,12 +385,12 @@ let source = {flan|
;; bootstrap hook at the foot of this file), and a macro has
;; no way to report a malformed call.
;; The name is Rust's `or_else`; the behaviour is Rust's `unwrap_or`, and the
;; split is deliberate. Flan has no laziness, so both arguments are already
;; evaluated when this is entered `or_else` there takes a closure and this
;; does not and `or-else` is the spelling that reads at the call site, where
;; the default is nearly always a literal or a constructor with nothing to
;; defer.
;; This is Java's `Optional.orElse`, hyphenated: eager, and it answers the
;; payload's type rather than another Option. Worth saying which, because Rust
;; spells something else `or_else` there it takes a closure and answers an
;; `Option<T>`, so borrowing that name for this behaviour would be the wrong
;; loan twice over. Java's own lazy sibling is `orElseGet`, which is the one
;; declined above.
;;
;; **At a $t that owns storage the result is a header copy, and the branch not
;; taken is still the caller's to free.** Since the copyable? repeal every

View File

@ -112,7 +112,10 @@
;; and it is written as a defn here because the *initialiser* is still refused
;; — "a global's value must be a compile-time constant — this one is computed"
;; — which is a separate piece of work on globals and nothing to do with
;; read-file or or-else. Everything inside the with-allocator is verbatim.
;; read-file or or-else. Everything inside the with-allocator is verbatim, so
;; the day a computed initialiser is allowed, load-game-data and the `set`
;; below it collapse back into the defvar above and this comment goes with
;; them.
;;
;; The texture path printed here has to be the one show-tileset printed, which
;; is what says read-file read the file and not merely something.

View File

@ -76,16 +76,14 @@
v))
;; An empty (Option (Vec u8)), which main needs to reach or-else's None branch
;; at a type that owns storage. It is a function rather than a bare None at the
;; call site because a bare None there would have to take its element type from
;; or-else's own signature, and the point is to arrive with the Option already
;; built — the same way edn/read arrives with one.
(defn opt-vec [full? bool] (Option (Vec u8))
(if full?
(let [v (vec-new u8)]
(push v 90)
(Some v))
None))
;; at a type that owns storage. It is a function and not a bare None at the
;; call site because a bare None there is refused — "nothing here says what
;; None is an Option of" — and a return type is one of the two places the
;; checker names as somewhere to say it. It is also the shape every real caller
;; is in: what arrives at or-else came out of something, the way edn/read's
;; answer does.
(defn none-vec [] (Option (Vec u8))
None)
;; (zeroed) takes its type from the position it is written in, so a variable
;; in that position is answered by the instantiation like any other type.
@ -174,7 +172,7 @@
(push full 65)
(push full 66)
(println (len (or-else (Some full) empty))) ; 2, full's header
(println (len (or-else (opt-vec false) empty))) ; 0, empty's
(println (len (or-else (none-vec) empty))) ; 0, empty's
(free full)
(free empty))

View File

@ -276,9 +276,10 @@
;; Option exists for. And this function has nothing to answer a FileError
;; *with* — `use-value` wants a path only the caller knows, and whether a
;; missing file is fatal or is a cue to write a default is the caller's policy
;; in every program. So slurp's condition and both its restarts arrive at the
;; caller's handler-bind unchanged, including a `use-value` that names another
;; path: the read then happens against that file and this function never knows.
;; in every program. Nothing here establishes a handler, so slurp's condition
;; reaches the caller's with both restarts still armed; edn-read.flan runs the
;; `use-value` half, where the handler names another path, the read happens
;; against that file, and this function never learns that anything went wrong.
(defn read-file [path string] (Option Value)
(let [src (slurp path (heap-allocator))]
(defer (free src))