Remove nth, the alias that was not one

nth and at were documented as the same operation, and as reads they were:
check.ml matched "at" | "nth" in one arm. But a place is recovered in two
other spots -- parse.ml for (set ...) and place_of_expr for (addr ...) --
and both match only Sym "at". So (set (nth a i) x) and (addr (nth a i))
were refused while the at forms worked.

Two names said to be identical that disagree about writing is worse than
one name, and the asymmetry is not worth fixing in three places to keep a
synonym. at is the indexing operation; nth is gone.

The six call sites were all reads, so they rewrite directly. get/put stay
the Map pair: get returns (Option V) and is deliberately not a place.

nth-gone.flan pins the removal -- it has to fail as a name nobody defined,
not quietly resolve to at again.

destructure~nth is compiler-generated and unrelated.
This commit is contained in:
Joseph Ferano 2026-09-12 04:46:41 +07:00
parent e416f28567
commit 5ea0bcae84
8 changed files with 26 additions and 9 deletions

View File

@ -120,6 +120,6 @@
(defn main [args [string]] i32
(if (< (len args) 2)
(do (print-line "usage: calc-me \"1 + 2 * 3\"") 1)
(match (evaluate (bytes (nth args 1)))
(match (evaluate (bytes (at args 1)))
(Some v) (do (print-f64 v) (print-line "") 0)
None (do (print-line "calc-me: cannot parse") 1))))

View File

@ -1128,7 +1128,7 @@ and named_call ctx ~want loc name args =
| other -> fail loc "len takes an array, a slice or a string, found %s"
(Types.to_string other));
prim Tast.Len index_ty [ a ]
| "at" | "nth" ->
| "at" ->
(match args with
| target :: idx when idx <> [] ->
let target = check ctx target in

View File

@ -104,7 +104,7 @@ world.
is type-directed: ~(defvar enemies (Map string Enemy) (map-new))~. ~get~
returns ~(Option V)~; ~put~ is the `Unit`-returning upsert. See
spec-memory.md for the deferred move-aware operations.
- Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~nth~, ~len~, ~update~.
- Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~at~, ~len~, ~update~.
Copying is explicit: ~(clone m)~, and owning containers move rather than copy on
assignment. No ~!~ convention — nothing is immutable, so it
would carry no information. No ~assoc~; it only existed as the copy-returning form.

View File

@ -82,7 +82,7 @@
(>= c 0) (< c (- cols 1))
(empty-at? r c)
(< (rand-f32) 0.5))
(set (at grid r c) (nth colors current-color))
(set (at grid r c) (at colors current-color))
(set (at velocity r c) 1.0)))))))
(defn move-grain [from-row i32 from-col i32
@ -198,7 +198,7 @@
rl/white)
(rl/draw-texture brush 20 50 rl/white)
(rl/draw-texture-v brush (rl/Vector2 {:x 44.0 :y 50.0})
(rl/get-color (nth colors current-color)))
(rl/get-color (at colors current-color)))
(rl/draw-texture-ex brush (rl/Vector2 {:x 72.0 :y 46.0}) 0.0 2.0 rl/white)))
;; The mirrored one beside them, scaled up so the flip is visible rather
;; than eight pixels wide. If the two badges look the same, either the flip
@ -449,7 +449,7 @@
;; and does not.
(defn draw-world-cursor []
(let [p (rl/get-screen-to-world-2d (rl/get-mouse-position) view)
tint (rl/get-color (nth colors current-color))
tint (rl/get-color (at colors current-color))
x (.x p)
y (.y p)
r (f32 (* brush-size cell-size))]
@ -528,7 +528,7 @@
sh (- (rl/get-screen-height) 40)]
(dotimes [i (len colors)]
(let [cx (- sw (* (- (len colors) (+ i 1)) 46))
c (rl/get-color (nth colors i))]
c (rl/get-color (at colors i))]
(rl/draw-circle cx sh (f32 16.0) c)
(when (= i current-color)
(rl/draw-circle-lines cx sh (f32 22.0) rl/white))))

View File

@ -58,7 +58,7 @@
;; this frame (spec-memory.md, non-escaping fn).
(defn largest [xs [a] gt (Fn [a a] bool)] (Option a)
(if (> (len xs) 0)
(Some (reduce (fn [x y] (if (gt x y) x y)) (nth xs 0) xs))
(Some (reduce (fn [x y] (if (gt x y) x y)) (at xs 0) xs))
None))
;; (largest hps >) — `>` at i32 is an ordinary function value

View File

@ -0,0 +1,11 @@
;;;; nth was an alias of at, and an asymmetric one: the checker accepted it as
;;;; a read, but parse.ml and place_of_expr both match only [at], so
;;;; (set (nth a i) x) and (addr (nth a i)) were refused while the [at] forms
;;;; worked. Two names documented as identical that disagree about writing are
;;;; worse than one name, so nth is gone and this pins the removal: it must
;;;; fail as an unknown name, not quietly resolve to at again.
(defvar a [4 i32])
(defn main [] i32
(nth a 0))

View File

@ -620,6 +620,11 @@ let () =
"one directory is one set of names";
refuses "two mains in one program" "programs/pkg-two-mains.flan"
"main is defined twice";
(* nth is gone, not renamed: it has to fail as a name nobody defined. If it
were ever re-added as an alias of [at] it would have to be a place too,
and this row is what says so. *)
refuses "nth is not a name" "programs/nth-gone.flan"
"unknown function nth";
(* ── wasm32 (NEXT.md, deferred item 6) ──────────────────────────────
The second target, and the reason sand-headless imports no raylib. What

View File

@ -643,8 +643,9 @@ second
<h2 id="arrays">Arrays and slices</h2>
<p><code>at</code> and <code>nth</code> are the same operation and take any number of
<p><code>at</code> indexes a fixed array or a slice, and takes any number of
indices, so <code>(at grid r c)</code> indexes a two-dimensional fixed array directly.
It is a place: <code>(set (at grid r c) v)</code> and <code>(addr (at grid r c))</code> both work.
<code>len</code> works on a fixed array, a slice or a string.
<code>(slice s lo hi)</code> takes a half-open range and never copies.</p>