Pin the () body guards, the match-arm rule, and correct two comments
This commit is contained in:
parent
fa2b56ba5a
commit
1702a62308
26
FIX.org
26
FIX.org
@ -1640,8 +1640,9 @@ The author, 2026-09-20, closing the question DISCUSS.org left open beside
|
|||||||
=(rl/with-drawing ())=: making bare ~()~ a unit value in expression position
|
=(rl/with-drawing ())=: making bare ~()~ a unit value in expression position
|
||||||
was considered and is not wanted. Empty forms doing the right thing — the
|
was considered and is not wanted. Empty forms doing the right thing — the
|
||||||
three above — covers the need that made it look attractive, and ~()~ stays
|
three above — covers the need that made it look attractive, and ~()~ stays
|
||||||
unspoken-for in value position on purpose, in case the language ever grows
|
unspoken-for in value position on purpose, against the possibility that the
|
||||||
lists: "we might want lists at some point."
|
language grows lists later and wants the spelling. (Paraphrased from the
|
||||||
|
author's note, not quoted.)
|
||||||
|
|
||||||
So ~()~ remains the type-position spelling of Unit and nothing else, and the
|
So ~()~ remains the type-position spelling of Unit and nothing else, and the
|
||||||
guard below is written against that rather than around it.
|
guard below is written against that rather than around it.
|
||||||
@ -1786,10 +1787,14 @@ with nothing after it, ~{a}~ — and that test is now two: the old one inverted
|
|||||||
to an ~accepts~, and a new one on ~{a}~.
|
to an ~accepts~, and a new one on ~{a}~.
|
||||||
|
|
||||||
Where it works: ~let~, and nowhere else, which is where ~{name .field}~ works
|
Where it works: ~let~, and nowhere else, which is where ~{name .field}~ works
|
||||||
today. Destructuring binds in ~let~ only — a defn parameter, an fn parameter, a
|
today. Destructuring binds in ~let~ only. A match arm is *not* a second
|
||||||
dotimes counter and *a match arm's binds* all take a plain name and are refused
|
position the shorthand had to reach: a struct pattern has never worked in one,
|
||||||
by [no_pattern] — so "let and match positions" from the brief has only one half
|
and the refusal there is the match grammar's own — "expected a pattern, found
|
||||||
to satisfy. The shorthand inherits that rule rather than changing it.
|
{a .x}" — not [no_pattern], which is what a defn parameter, an fn parameter
|
||||||
|
and a dotimes counter get. Checked against the compiler rather than read off
|
||||||
|
parse.ml's comment: ~{.x .y}~ and ~{a .x}~ in a match arm produce the same
|
||||||
|
refusal as each other, which is the claim that matters — the shorthand
|
||||||
|
inherited the existing rule rather than changing it.
|
||||||
|
|
||||||
An unknown field gets the named form's refusal unchanged, because it is the
|
An unknown field gets the named form's refusal unchanged, because it is the
|
||||||
same field access underneath: "Point has no field z" with the declared_note
|
same field access underneath: "Point has no field z" with the declared_note
|
||||||
@ -1802,7 +1807,14 @@ listing the fields there are.
|
|||||||
accepted at a ~(Fn [] ())~ want and refused at a ~(Fn [] i32)~ one; the
|
accepted at a ~(Fn [] ())~ want and refused at a ~(Fn [] i32)~ one; the
|
||||||
~{.x .y}~ shorthand accepted plain, mixed with a pair, and nested; ~{.z}~
|
~{.x .y}~ shorthand accepted plain, mixed with a pair, and nested; ~{.z}~
|
||||||
refused by field name; ~{.x}~ inverted from a refusal to an ~accepts~; ~{a}~
|
refused by field name; ~{.x}~ inverted from a refusal to an ~accepts~; ~{a}~
|
||||||
refused.
|
refused; and both ~{.x .y}~ and ~{a .x}~ refused identically in a match arm,
|
||||||
|
which is the "wherever the named form works" half of the claim.
|
||||||
|
- test/programs/rl-with-empty.flan and rl-with-empty-arg.flan, through
|
||||||
|
test_acceptance's ~refuses~: the two guard shapes, a body starting at
|
||||||
|
argument zero and a body starting after a camera, each given a bare ~()~
|
||||||
|
and each answering the name the zero-argument case already answered. Never
|
||||||
|
built, which is how rl-with-reject.flan beside them works and is why these
|
||||||
|
need no raylib on the machine.
|
||||||
- test/programs/prelude-macros.flan, plain and -O0: ~comment~ with four
|
- test/programs/prelude-macros.flan, plain and -O0: ~comment~ with four
|
||||||
different kinds of garbage in it; inc/dec over eleven types; ++/-- over a
|
different kinds of garbage in it; inc/dec over eleven types; ++/-- over a
|
||||||
local, a field, an element and a deref; empty ~when~ and ~unless~ bodies.
|
local, a field, an element and a deref; empty ~when~ and ~unless~ bodies.
|
||||||
|
|||||||
@ -2031,9 +2031,9 @@ let source = {flan|
|
|||||||
;; The one thing the compiler could say and this cannot is a reason. A macro
|
;; The one thing the compiler could say and this cannot is a reason. A macro
|
||||||
;; has no error facility: it runs inside the compiler and anything it signals
|
;; has no error facility: it runs inside the compiler and anything it signals
|
||||||
;; aborts the compile with no location. So a malformed (unless) answers a name
|
;; aborts the compile with no location. So a malformed (unless) answers a name
|
||||||
;; nothing defines, and the report is "unknown name unless-takes-a-test-and-a-
|
;; nothing defines, and the report is "unknown name unless-takes-a-test" at the
|
||||||
;; body" at the call site, which is the right place and the wrong sentence.
|
;; call site, which is the right place and the wrong sentence. That is the next
|
||||||
;; That is the next thing a macro needs and it is written down in NEXT.md.
|
;; thing a macro needs and it is written down in NEXT.md.
|
||||||
;;
|
;;
|
||||||
;; An empty body is allowed, and expands to the (do) it always would have:
|
;; An empty body is allowed, and expands to the (do) it always would have:
|
||||||
;; (unless test) is a guard whose body has not been written yet, which is a
|
;; (unless test) is a guard whose body has not been written yet, which is a
|
||||||
|
|||||||
21
test/programs/rl-with-empty-arg.flan
Normal file
21
test/programs/rl-with-empty-arg.flan
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
;;;; The same bare-() body, on a macro that takes an argument before it.
|
||||||
|
;;;;
|
||||||
|
;;;; Two guard shapes, so two programs. with-drawing's body starts at argument
|
||||||
|
;;;; zero; with-mode-2d's starts at argument one, after the camera, so the
|
||||||
|
;;;; check is "exactly the minimum arguments, and the last of them is ()"
|
||||||
|
;;;; rather than "one argument and it is ()". A guard written only for the
|
||||||
|
;;;; first shape would leave the four siblings that take an argument exactly
|
||||||
|
;;;; where with-drawing was.
|
||||||
|
;;;;
|
||||||
|
;;;; The camera is real here on purpose: the refusal has to be about the body
|
||||||
|
;;;; and not about the camera, and a () in the camera position is deliberately
|
||||||
|
;;;; not this guard's business.
|
||||||
|
;;;;
|
||||||
|
;;;; Being refused is the whole test; this is never built.
|
||||||
|
|
||||||
|
(import rl "vendor:raylib")
|
||||||
|
|
||||||
|
(defn main [] i32
|
||||||
|
(let [c (rl/Camera2D {})]
|
||||||
|
(rl/with-mode-2d c ()))
|
||||||
|
0)
|
||||||
21
test/programs/rl-with-empty.flan
Normal file
21
test/programs/rl-with-empty.flan
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
;;;; with-drawing given a body that is a bare ().
|
||||||
|
;;;;
|
||||||
|
;;;; The other way a body can be missing, and the one that used to get through.
|
||||||
|
;;;; rl-with-reject.flan beside this file is the zero-argument case, which the
|
||||||
|
;;;; guard always caught; one argument that happens to be () is one argument,
|
||||||
|
;;;; so it was spliced into the expansion verbatim and the refusal came out of
|
||||||
|
;;;; the middle of the expanded (do) — "() is not an expression", several forms
|
||||||
|
;;;; from anything anyone wrote.
|
||||||
|
;;;;
|
||||||
|
;;;; () has no value-position meaning in the language at all, so a lone one
|
||||||
|
;;;; where a body goes is never a body, and the guard answers the same name the
|
||||||
|
;;;; missing-body case already answered. (do) is what to write for a body that
|
||||||
|
;;;; is meant to be empty.
|
||||||
|
;;;;
|
||||||
|
;;;; Being refused is the whole test; this is never built.
|
||||||
|
|
||||||
|
(import rl "vendor:raylib")
|
||||||
|
|
||||||
|
(defn main [] i32
|
||||||
|
(rl/with-drawing ())
|
||||||
|
0)
|
||||||
@ -2515,6 +2515,19 @@ let () =
|
|||||||
"programs/rl-with-reject.flan"
|
"programs/rl-with-reject.flan"
|
||||||
"with-mode-2d-takes-a-camera-and-a-body";
|
"with-mode-2d-takes-a-camera-and-a-body";
|
||||||
|
|
||||||
|
(* The other spelling of a body that was not written, and the reason these
|
||||||
|
are two programs rather than one: the guard has two shapes. A body that
|
||||||
|
starts at argument zero is "one argument and it is ()"; a body that
|
||||||
|
starts after a camera is "exactly the minimum arguments, and the last
|
||||||
|
of them is ()". Both answer the same name the zero-argument case above
|
||||||
|
answers, because it is the same mistake. *)
|
||||||
|
refuses "with-drawing given a bare () for a body"
|
||||||
|
"programs/rl-with-empty.flan"
|
||||||
|
"with-drawing-takes-a-body";
|
||||||
|
refuses "with-mode-2d given a bare () for a body"
|
||||||
|
"programs/rl-with-empty-arg.flan"
|
||||||
|
"with-mode-2d-takes-a-camera-and-a-body";
|
||||||
|
|
||||||
(* Visibility: main is not a name a package offers, and saying so is the
|
(* Visibility: main is not a name a package offers, and saying so is the
|
||||||
point — "unknown name sand/main" would be true and useless. *)
|
point — "unknown name sand/main" would be true and useless. *)
|
||||||
(* Generics, at the definition rather than at a call site. Both of these
|
(* Generics, at the definition rather than at a call site. Both of these
|
||||||
|
|||||||
@ -2652,6 +2652,18 @@ let () =
|
|||||||
rejects_check "a name with no field after it"
|
rejects_check "a name with no field after it"
|
||||||
(pt ^ "(defn f [p Point] i32 (let [{a} p] 0))")
|
(pt ^ "(defn f [p Point] i32 (let [{a} p] 0))")
|
||||||
~needle:"has no .field";
|
~needle:"has no .field";
|
||||||
|
(* And where the shorthand does *not* reach, which is not a limitation it
|
||||||
|
introduced: a struct pattern has never worked in a match arm, and the two
|
||||||
|
spellings are refused identically there. Pinned as a pair, because "the
|
||||||
|
shorthand works wherever {name .field} works" is the claim, and a row on
|
||||||
|
only one of them would not be saying it. *)
|
||||||
|
List.iter
|
||||||
|
(fun pat ->
|
||||||
|
rejects_check ("a struct pattern in a match arm: " ^ pat)
|
||||||
|
(pt ^ Printf.sprintf
|
||||||
|
"(defn f [p Point] i32 (match p %s 0))" pat)
|
||||||
|
~needle:"expected a pattern, found")
|
||||||
|
[ "{.x .y}"; "{a .x}" ];
|
||||||
|
|
||||||
rejects_check "a field the struct does not have"
|
rejects_check "a field the struct does not have"
|
||||||
(pt ^ "(defn f [p Point] i32 (let [{:keys [x z]} p] (+ x z)))")
|
(pt ^ "(defn f [p Point] i32 (let [{:keys [x z]} p] (+ x z)))")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user