The refused shape is defmacro at C-x C-e, and the union's boundary is defn's
The head dispatch already names it; test_repl pins it beside the defvar case, and the editor-typed defmacro moved below the describe assertion that says an evaluation changes nothing -- that one does, on purpose. BUILT.md gains the boundary of the shape: the set only grows, a deleted defmacro still expands, and that is exactly what a deleted defn already does.
This commit is contained in:
parent
b9f5b5c44c
commit
9fc04193d9
14
BUILT.md
14
BUILT.md
@ -5453,6 +5453,13 @@ since.** Applied to macros, that is both halves of the fix and neither is option
|
|||||||
this path — it parses to a `Defn` and installs a body like any other. The only thing missing was the session
|
this path — it parses to a `Defn` and installs a body like any other. The only thing missing was the session
|
||||||
remembering that the name is a macro.
|
remembering that the name is a macro.
|
||||||
|
|
||||||
|
**The boundary of that shape, written down rather than discovered:** the set only ever grows. Delete
|
||||||
|
`(defmacro tenfold ...)` from the buffer, reload, and `(tenfold 7)` still expands, because a union never removes.
|
||||||
|
That is not a leak to fix later — it is exactly what `defn` does. `Session.eval` keeps `kept @ added`, so a
|
||||||
|
function the file no longer declares is still in the session and still callable, and the running process still has
|
||||||
|
its body loaded. A session is what the program was built from plus every change *accepted*, and a deletion is not
|
||||||
|
a change anything sent. The one operation that forgets is restarting the session, which is what it is for.
|
||||||
|
|
||||||
**The session does not re-read the file, and that was the live alternative.** It has `~origin`, the buffer's own
|
**The session does not re-read the file, and that was the live alternative.** It has `~origin`, the buffer's own
|
||||||
path, and re-reading would pick up macros the session has never been told about. It would also read whatever is
|
path, and re-reading would pick up macros the session has never been told about. It would also read whatever is
|
||||||
*saved*, while the buffer on screen is whatever is *typed* — so an expansion would silently use a body the reader
|
*saved*, while the buffer on screen is whatever is *typed* — so an expansion would silently use a body the reader
|
||||||
@ -5486,6 +5493,13 @@ The names stay unqualified. A buffer writes its own macro's bare name, so that i
|
|||||||
answer to; a file inside a package the program also imports ends up holding both, the bare one from here and
|
answer to; a file inside a package the program also imports ends up holding both, the bare one from here and
|
||||||
`alias/name` from `Load`, which is what the two call sites each need.
|
`alias/name` from `Load`, which is what the two call sites each need.
|
||||||
|
|
||||||
|
**One shape stays refused, and its refusal was already right.** `(defmacro ...)` at `C-x C-e` is a declaration,
|
||||||
|
and `Parse.expr`'s head dispatch already names `defmacro` first among the heads it refuses — "defmacro is a
|
||||||
|
top-level declaration, not an expression". It matters more now than when it was written: before this, nobody would
|
||||||
|
type a `defmacro` at `C-x C-e`, because the session forgot it either way. Now that one typed at the editor means
|
||||||
|
something, reaching for `C-x C-e` on it is a reflex, and what comes back says what it is rather than complaining
|
||||||
|
about an unknown function. `test_repl` pins it beside the `defvar` case.
|
||||||
|
|
||||||
Both editor paths work and they are different wraps — `Parse.expr`'s for `C-x C-e` and `Parse.decl`'s for
|
Both editor paths work and they are different wraps — `Parse.expr`'s for `C-x C-e` and `Parse.decl`'s for
|
||||||
`C-c C-c`. `test/test_session.ml` covers both, plus a `defmacro` the file never had followed by a call to it,
|
`C-c C-c`. `test/test_session.ml` covers both, plus a `defmacro` the file never had followed by a call to it,
|
||||||
plus editing that macro and calling it again; the assertions are on the IR and not on the absence of an
|
plus editing that macro and calling it again; the assertions are on the IR and not on the absence of an
|
||||||
|
|||||||
@ -168,19 +168,6 @@ let () =
|
|||||||
same read that built the program. Over a real socket, because that is
|
same read that built the program. Over a real socket, because that is
|
||||||
the path a person is on; test_session has the in-process halves. *)
|
the path a person is on; test_session has the in-process halves. *)
|
||||||
value "the file's own macro" "(tenfold 7)" "70";
|
value "the file's own macro" "(tenfold 7)" "70";
|
||||||
(* A macro the file never had, typed at the editor and then called. This
|
|
||||||
is the shape the fix chose: a [defmacro] evaluated into a session
|
|
||||||
*joins* it, exactly as a [defn] does, and the next evaluation can call
|
|
||||||
it. Two round trips, because that is the whole of the claim. *)
|
|
||||||
(let r =
|
|
||||||
request c
|
|
||||||
(Printf.sprintf "(:op \"eval\" :code %s :file \"/tmp/buf.flan\")"
|
|
||||||
(quote "(defmacro thrice [args] `(* ~(at args 0) 3))"))
|
|
||||||
in
|
|
||||||
if status r <> "ok" then
|
|
||||||
fail "evaluating a defmacro over the socket: %s"
|
|
||||||
(Option.value ~default:(status r) (field r "message")));
|
|
||||||
value "a macro defined at the editor" "(thrice 14)" "42";
|
|
||||||
|
|
||||||
(* The one that proves it ran inside the process: the program increments
|
(* The one that proves it ran inside the process: the program increments
|
||||||
[ticks] every frame, so two evaluations of it must disagree. A copy
|
[ticks] every frame, so two evaluations of it must disagree. A copy
|
||||||
@ -215,12 +202,36 @@ let () =
|
|||||||
refuses "a declaration inside an expression" "(do 1 (defn f [] i32 1))"
|
refuses "a declaration inside an expression" "(do 1 (defn f [] i32 1))"
|
||||||
"top-level declaration";
|
"top-level declaration";
|
||||||
refuses "an unknown name" "no-such-name" "unknown name";
|
refuses "an unknown name" "no-such-name" "unknown name";
|
||||||
|
(* [defmacro] is in that same head list, and it is the shape that stays
|
||||||
|
refused now that a [defmacro] typed at the editor means something: a
|
||||||
|
person will reach for C-x C-e on one by reflex, and what they get is
|
||||||
|
the sentence naming it a declaration rather than an arity complaint
|
||||||
|
about an unknown function. C-c C-c is where a declaration goes, which
|
||||||
|
is the case below. *)
|
||||||
|
refuses "a defmacro at C-x C-e" "(defmacro m [args] args)"
|
||||||
|
"top-level declaration";
|
||||||
|
|
||||||
(* And the session is untouched by all of it: an evaluation is not a
|
(* And the session is untouched by all of it: an evaluation is not a
|
||||||
declaration, so nothing named eval/N accumulates in the program. *)
|
declaration, so nothing named eval/N accumulates in the program. *)
|
||||||
let r = request c "(:op \"describe\")" in
|
let r = request c "(:op \"describe\")" in
|
||||||
if status r <> "ok" then fail "describe after evaluating: %s" (status r);
|
if status r <> "ok" then fail "describe after evaluating: %s" (status r);
|
||||||
|
|
||||||
|
(* Below the line above deliberately, because this one *does* change the
|
||||||
|
session: a macro the file never had, typed at the editor and then
|
||||||
|
called. It is the shape the fix chose — a [defmacro] evaluated into a
|
||||||
|
session joins it, exactly as a [defn] does, and the next evaluation
|
||||||
|
can call it. Two round trips, because that is the whole of the
|
||||||
|
claim. *)
|
||||||
|
(let r =
|
||||||
|
request c
|
||||||
|
(Printf.sprintf "(:op \"eval\" :code %s :file \"/tmp/buf.flan\")"
|
||||||
|
(quote "(defmacro thrice [args] `(* ~(at args 0) 3))"))
|
||||||
|
in
|
||||||
|
if status r <> "ok" then
|
||||||
|
fail "evaluating a defmacro over the socket: %s"
|
||||||
|
(Option.value ~default:(status r) (field r "message")));
|
||||||
|
value "a macro defined at the editor" "(thrice 14)" "42";
|
||||||
|
|
||||||
ignore (request c "(:op \"close\")");
|
ignore (request c "(:op \"close\")");
|
||||||
Unix.close c
|
Unix.close c
|
||||||
end;
|
end;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user