flan/test/programs/struct-ergonomics.flan
Joseph Ferano d6fc15474b The count is length, so len is a name a program can have
The author: "I think I prefer length over len, because then I'll use len as
the variable name". One arm in check.ml, one row in the table beside it, and
every (len x) in lib, test, examples, vendor, spike, docs, web, emacs,
plan.org and NEXT.md rewritten.

Shadowing and builtin/ had already taken most of the sting out: a (defn len
...) was legal and won in its own file, and builtin/len reached past it. What
was left is that len was still a builtin — the defn earned a warning, and a
wrapper had to say builtin/ at every inner call. Now there is nothing under
the short name: len is an ordinary identifier in every position, which is
what (let [len (length xs)] ...) wants.

length takes over as shadowing's worked example rather than the feature
losing one. shadow-builtin.flan, builtin-qualified.flan, pkgs/shadowed and the
builtin/ rows in test_flan move to it and go on testing shadowing.

A call to a len nothing defines is answered where an unknown function is,
after every table and after the shadowing guard, so a program with its own len
never reaches it. The sentence is said rather than guessed at — len and length
are three edits apart and the did-you-mean's net is one — and the call is
written back out through spell_arg, as-slice's spelling lifted out of it and
now shared, so what is printed compiles.

sand.flan:33 still calls the old name and is the author's to change; until it
does, test_acceptance and test_session abort there. Both were run green
against a copy with that one line changed. FIX.org says so.
2026-09-21 11:58:56 +07:00

71 lines
2.9 KiB
Plaintext

;;;; Two spellings a struct value gained, both decided by the checker.
;;;;
;;;; A bare {.field v} has no type written in front of it. The fields alone do
;;;; not name one, but the position usually does: a defn's return type, a
;;;; parameter, a field of an enclosing literal, a place being set. The parser
;;;; cannot see any of that -- it has no symbol table and no expectation -- so
;;;; it builds the field list and the checker reads the type off the want and
;;;; hands the very same list to the named form's own code. Everything below
;;;; that follows -- ZII for an omitted field, the unknown-field and
;;;; duplicate-field refusals -- is therefore not a copy of the named form's
;;;; rules, it is the named form's rules.
;;;;
;;;; (Cell 1 2) is the other half, and it is a call until the checker looks the
;;;; head up: a struct name where a function name would be. Arity is exact.
;;;; ZII is still available and is what the braces do; what a positional list
;;;; cannot do is SAY which field it left out, so it is not allowed to leave
;;;; one out.
(defstruct Cell [row i32 col i32])
(defstruct Grid [a Cell b Cell])
;; The case from the notes: a defn whose return type is the only place the
;; type is written. This is what used to fail at parse, before any checking.
(defn origin [] Cell {.row 0 .col 0})
;; An omitted field is zeroed, exactly as (Cell {.row 3}) zeroes .col.
(defn just-row [r i32] Cell {.row r})
;; A parameter is an expectation too -- as the only argument and as a later
;; one, which are two different paths into the checker and both arrive here.
(defn sum [c Cell] i32 (+ (.row c) (.col c)))
(defn offset-sum [n i32 c Cell] i32 (+ n (sum c)))
;; A field of an enclosing literal. The inner braces are bare and the outer
;; ones are not, so both readings stand side by side in one form.
(defn grid [] Grid (Grid {.a {.row 1 .col 2} .b (Cell 3 4)}))
;; Positional, in the position that names the type anyway.
(defn diag [n i32] Cell (Cell n n))
;; Nested positional, and positional feeding a bare literal's field.
(defn pair [] Grid (Grid {.a (Cell 5 6) .b (Cell 7 8)}))
(defn main [] ()
(println (sum (origin)))
(println (sum (just-row 9)))
(println (sum {.row 10 .col 20}))
(println (offset-sum 1 {.row 2}))
(let [g (grid)]
(println (.row (.a g)))
(println (.col (.a g)))
(println (.row (.b g)))
(println (.col (.b g))))
(println (sum (diag 11)))
(let [p (pair)]
(println (sum (.a p)))
(println (sum (.b p))))
;; A bare literal set into a typed place: the place's type is the want.
(let [c (Cell 0 0)]
(set c {.row 7 .col 8})
(println (sum c))
;; And into a field, whose type is the want one level down.
(let [g (Grid {.a c .b c})]
(set (.b g) {.row 100})
(println (sum (.b g)))))
;; The dyn map literal is untouched by any of this: keyword keys, and a
;; .field-keyed brace was never part of that spelling.
(let [m {:a 1 :b 2}]
(println (length m))
(println (get m :b))))