The last enum member takes its prefix, and the alias goes

TraceLogLevel carried `warning` beside `log-warning` so that sand.flan's
one call kept working while the rename landed. The review found it was not
inert: render.ml folds members last-wins, so a 4 read back as :warning in
the break loop, the did-you-mean offered it, and every TraceLogLevel error
listed it. A spelling nobody should write was the one the compiler taught.

So the call is respelled and the alias is gone. Every member of every mapped
enum carries its enum's prefix now, with no exception to explain.
This commit is contained in:
Joseph Ferano 2026-09-21 09:32:37 +07:00
parent da987169c8
commit 10b1abbf09
6 changed files with 49 additions and 37 deletions

View File

@ -395,8 +395,39 @@ list (surfaced same as any other diagnostic) for every site where a
conversion happened that wasn't an explicit cast. Not designed, and a real conversion happened that wasn't an explicit cast. Not designed, and a real
philosophy question for the language, not just an implementation task. philosophy question for the language, not just an implementation task.
** pos?/neg?/zero? don't exist, and need to be generic over numeric types ** CORRECTION: pos?/neg?/zero? already exist, and generics already work
Nothing named pos?/neg?/zero?/sign anywhere in the prelude. Trivial to write I was wrong on both counts in the original entry below (kept struck-through
for the record rather than deleted).
pos?/neg?/zero? are already in lib/prelude.ml:493-506, and they're already
generic: `(defn pos? [x $t] bool {:where (numeric? $t)} (> x 0))` — same for
neg?/zero?. My "nothing named pos?/neg?" search was wrong because my own
grep pattern used bare `pos?`/`neg?` — the `?` is an unescaped regex
quantifier there ("s is optional"), not a literal character, so the search
silently missed every hit. Should have quoted/escaped it.
And generics DO exist and are a real, working, monomorphized feature —
Odin-style: `$t` in a defn's parameter vector binds a type variable, bare
`t` in the body/return type reads it, `{:where (pred? $t)}` after the return
type constrains it, and the checker instantiates a fresh copy per concrete
type actually used at a call site (an instantiation cache, like Odin's
gen_procs — lib/check.ml:137-162). swap/reverse/sort/min-of/max-of/
or-else/reduce/filter/pos?/neg?/zero? in the prelude all use exactly this.
The "generic code over the type variable %s... milestone 5" message I
quoted also looks to have been rewritten since — lib/check.ml:1069-1092 now
explicitly says "generics were milestone 5 work. They are not" and gives a
much clearer message naming the fix ("write $%s in the parameter vector to
introduce one"). Also added since: `foreign_spelling` (lib/check.ml:301-312)
maps common other-language names (integer/uint/long/double/str/etc.) to
flan's real names — though note `int` and `float` themselves specifically
are still NOT in that list, so those two still fall through to the
type-variable-not-introduced message rather than a direct "unknown type int
— Flan spells it i32". Real change happened, but not 100% coverage of what
was originally reported.
Original (wrong) entry, kept for the record:
~~Nothing named pos?/neg?/zero?/sign anywhere in the prelude. Trivial to write
per-type ((defn pos? [x i64] bool (> x 0))), but that's the problem: we want per-type ((defn pos? [x i64] bool (> x 0))), but that's the problem: we want
these (and inc/dec from earlier) to work across every numeric type without these (and inc/dec from earlier) to work across every numeric type without
writing one copy per type. writing one copy per type.
@ -410,7 +441,7 @@ not written as generic Flan functions), which is the current workaround for
user-defined functions like pos?/neg?/inc/dec. Two related asks logged user-defined functions like pos?/neg?/inc/dec. Two related asks logged
together: (1) add pos?/neg?/zero?, (2) make milestone-5 generics real so together: (1) add pos?/neg?/zero?, (2) make milestone-5 generics real so
functions like these don't need special-casing into the compiler to be functions like these don't need special-casing into the compiler to be
generic. generic.~~
** println output goes to *flan-output*, not inline in the repl ** println output goes to *flan-output*, not inline in the repl
Deliberate per emacs/flan-repl.el:40-44: "a value and the program's output Deliberate per emacs/flan-repl.el:40-44: "a value and the program's output

29
FIX.org
View File

@ -4721,21 +4721,16 @@ test/test_flan.ml pins one member of each of the eleven against the C name
the rule reaches, read out of the real vendor/raylib/bindings, plus the claim the rule reaches, read out of the real vendor/raylib/bindings, plus the claim
that every mapped enum declares a prefix at all. that every mapped enum declares a prefix at all.
** sand.flan, and the alias that is standing in for it — author's to remove ** sand.flan, and the alias that stood in for it — removed at the merge
sand.flan line 121 is ~(rl/set-trace-log-level :warning)~, and the file is The lane could not edit sand.flan, whose line 121 was
the author's live WIP, so this lane did not touch it. The default suite ~(rl/set-trace-log-level :warning)~, so it left ~warning 4~ beside
compiles sand.flan (test_session evaluates it whole), so the rename would ~log-warning 4~ in the TraceLogLevel defenum with a ~constant
have left that red. TraceLogLevel/warning LOG_WARNING~ line to match. The review found the alias
was not inert: ~lib/render.ml~ folds members so the last-declared wins, so a
TraceLogLevel of 4 read back as ~:warning~ in the break loop, the did-you-mean
suggested it, and every TraceLogLevel error listed it among the members.
Instead TraceLogLevel carries ~warning 4~ beside ~log-warning 4~ — a defenum So it is gone, with the call respelled, at the merge: sand.flan:121 says
allows the same value twice — with ~constant TraceLogLevel/warning ~:log-warning~, the ~warning 4~ member and its paragraph are out of
LOG_WARNING~ in ~bindings~ so the header check still reaches it. It is the raylib.flan, and the ~constant~ line is out of bindings. Every member of every
one member in the package that does not carry its enum's prefix, and it mapped enum now carries its prefix, with no exception.
exists only for that one call.
To remove it, three edits together:
1. sand.flan:121 → ~(rl/set-trace-log-level :log-warning)~
2. vendor/raylib/raylib.flan — delete ~warning 4~ from the TraceLogLevel
defenum, and the paragraph above it that explains the alias
3. vendor/raylib/bindings — delete the ~constant TraceLogLevel/warning~ line
and its comment

View File

@ -118,7 +118,7 @@
[(FileError [c] nil)])) [(FileError [c] nil)]))
(defn main [] () (defn main [] ()
(rl/set-trace-log-level :warning) (rl/set-trace-log-level :log-warning)
(rl/init-window screen-width screen-height "SAND") (rl/init-window screen-width screen-height "SAND")
(defer (rl/close-window)) (defer (rl/close-window))
(rl/set-target-fps 120) (rl/set-target-fps 120)

View File

@ -1923,7 +1923,7 @@ let () =
ptr+len. /tmp is written to, and both optimisation levels write the ptr+len. /tmp is written to, and both optimisation levels write the
same bytes, so the shared name is harmless. same bytes, so the shared name is harmless.
Trace logging stays at :warning and no read here is out of bounds, so Trace logging stays at :log-warning and no read here is out of bounds, so
a warning appearing in this output is a real failure [run] folds a warning appearing in this output is a real failure [run] folds
stderr in. *) stderr in. *)
let raylib_image_out = let raylib_image_out =

View File

@ -247,12 +247,6 @@ enum PixelFormat PIXELFORMAT_ pixel-
# hatch: one name the prefix rule gets wrong, said once. # hatch: one name the prefix rule gets wrong, said once.
constant Gesture/gesture-double-tap GESTURE_DOUBLETAP constant Gesture/gesture-double-tap GESTURE_DOUBLETAP
# TraceLogLevel's members are `log-`, and `warning` beside `log-warning` is
# the one member that is not. It is an alias kept for sand.flan's one call,
# which this lane is not allowed to edit; both it and this line go when that
# call becomes `:log-warning`.
constant TraceLogLevel/warning LOG_WARNING
# And the second such pair, for the same kind of reason. The rule uppercases # And the second such pair, for the same kind of reason. The rule uppercases
# the Flan member name, and raylib spells the two ASTC block sizes with a # the Flan member name, and raylib spells the two ASTC block sizes with a
# lowercase `x` — PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA — where every other # lowercase `x` — PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA — where every other

View File

@ -107,17 +107,9 @@
;; `log-` rather than `trace-`: the enum is TraceLogLevel but the C names are ;; `log-` rather than `trace-`: the enum is TraceLogLevel but the C names are
;; LOG_, `trace` is itself a member, and :log-warning is what the call reads ;; LOG_, `trace` is itself a member, and :log-warning is what the call reads
;; as. ;; as.
;;
;; `warning` beside `log-warning` is the same value twice, which a defenum
;; allows, and it is the one member in this file that does not carry its
;; enum's prefix. It is an alias for sand.flan's single
;; `(rl/set-trace-log-level :warning)` — the file this lane was told not to
;; touch — and goes, with its `constant` line in `bindings`, when that call
;; is respelled.
(defenum TraceLogLevel (defenum TraceLogLevel
[log-all 0 log-trace 1 log-debug 2 log-info 3 log-warning 4 [log-all 0 log-trace 1 log-debug 2 log-info 3 log-warning 4
log-error 5 log-fatal 6 log-none 7 log-error 5 log-fatal 6 log-none 7])
warning 4])
;; ── Window ────────────────────────────────────────────────────────── ;; ── Window ──────────────────────────────────────────────────────────