* `eval'ing form in the current lexical environment @ 2024-07-07 18:55 Thuna 2024-07-07 19:16 ` Stefan Monnier via Users list for the GNU Emacs text editor 0 siblings, 1 reply; 7+ messages in thread From: Thuna @ 2024-07-07 18:55 UTC (permalink / raw) To: help-gnu-emacs How would I `eval' a form as though it were where the `(eval ...)' form was? That is, is there an argument to LEXICAL I can pass such that (... (eval form LEXICAL) ...) becomes identical to (... <value-of-form> ...) Let me state my problem as well, in case anyone knows of a solution to that: I have a macro which modifies the form fed to it, macroexpanding it as necessary. Functions are handled specifically, in that its arguments are computed and the call to the function replaced by (func arg1 arg2...) If, however, `func' is then redefined as a macro, and uses one of its args without evaluating it, then this would understandably break. At the same time, the process the original form went through in my macro is one that this now-macro form needs to go through as well, so I expand into a form which *during evaluation* calls this process on the literal form which is fed to it and evals it. This all looks something like (defmacro (lambda-list head &rest args) `(defun ,lambda-list ... (if (macrop #',head) (eval (<processor> '(,head ,@args))) (,head ,@argsyms)))) and it is absolutely expected for the forms in ARGS to be aware of and reference the variables established in LAMBDA-LIST, but because of the above problem that is not the case (as far as I could tell). ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-07 18:55 `eval'ing form in the current lexical environment Thuna @ 2024-07-07 19:16 ` Stefan Monnier via Users list for the GNU Emacs text editor 2024-07-08 2:07 ` Thuna 0 siblings, 1 reply; 7+ messages in thread From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2024-07-07 19:16 UTC (permalink / raw) To: help-gnu-emacs > How would I `eval' a form as though it were where the `(eval ...)' form > was? That is, is there an argument to LEXICAL I can pass such that > (... (eval form LEXICAL) ...) > becomes identical to > (... <value-of-form> ...) No. If you know the relevant set of variables, you can do (eval form `((VAR1 . ,VAL1) (VAR2 . ,VAL2) ...)) but why not just put `form` in there? Why exactly do you need `eval` in the generated code? > (defmacro (lambda-list head &rest args) > `(defun ,lambda-list > ... > (if (macrop #',head) > (eval (<processor> '(,head ,@args))) > (,head ,@argsyms)))) I'm afraid I had trouble following your explanation (not fault of yours: it's just difficult to describe in prose what a code does, especially when it itself manipulates code). Could you show an example that illustrates why you need `eval` in there? My naive self would tell you to use (defmacro (lambda-list head &rest args) `(defun ,lambda-list ... (if (macrop #',head) ,(<processor> `(,head ,@args)) (,head ,@argsyms)))) Or maybe your "procesor" needs to traverse the code, and what you really need is to macro-expand it beforehand, something like: (defmacro (lambda-list head &rest args) (let* ((code (macroexpand-all (... `(,head ,@args) ...) macroexpand-all-environment)) (processed (<processor> code))) `(defun ,lambda-list ... ,code ...))) - Stefan ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-07 19:16 ` Stefan Monnier via Users list for the GNU Emacs text editor @ 2024-07-08 2:07 ` Thuna 2024-07-08 2:49 ` Stefan Monnier 0 siblings, 1 reply; 7+ messages in thread From: Thuna @ 2024-07-08 2:07 UTC (permalink / raw) To: Stefan Monnier; +Cc: help-gnu-emacs [ I appear to have forgotten to CC the list, sorry about that. Below's what I have previously sent. Please only reply to this mail. ] Hmm, I now realize that I misspoke in my original post. What is doing this is not a macro but a function, so that was why I went to `eval'. This form is being generated from within <processor> itself. Now, your suggestion did bring up an interesting point for me in that I could simply make a wrapper macro for this function and put that in, but there does appear to be a problem with this approach in this specific situation which... will be even more difficult to explain. Below are two versions of me trying to explain the problem, one with context and one with me attempting to reduce it down to the bare essentials. I'll leave the former in in case I leave out some important details in the latter, but I have to be honest: it is extremely convoluted. Please only read it if the first explanation doesn't make sense or if you wanted to take a look at the code and find yourself confused as to what's happening in it. --- Reduced down to essentials --- The function (which I accidentally called a macro) we are talking about is the <processor> itself, that is, it looks something like (defun <processor> (form) ;; `(,head ,@args) == form `(... (if (macrop #',head) (eval (<processor> `(,head ,@args))) ... `(,head ,@argsyms)))) and there is a different macro which expands into the defun by doing (defmacro (name lambda-list &rest body) `(defun ,name ,lambda-list ,(<processor> `(progn ,@body)))) <processor> recursively expands and modifies FORM, and returns a version of it which is "processed", and then the definition of NAME is set to be that processed body. All macros are expanded and processed, thus `(,head ,@args) above is /necessarily/ a function call... that is, unless it is later redefined as a macro. The goal with (if (macrop #',head) ...) is to check for this exact case, an in case it DID happen, fix it by processing the form again, calling that processed version instead of what was supposed to happen. This however can only during the runtime of NAME, because that is when we are concerned with `head' being a macro, so the call to <processor> must happen within NAME and not within the top-level <processor>. Because we are calling <processor> during NAME, we obtain a form which needs to be evaluated. --- With context --- Let me provide you with the actual code we are discussing. I intend to bring this up in emacs-devel at some point anyways so hopefully it'll be worth the read: https://git.sr.ht/~thuna/tcl-values Quick rundown of what the code does: This is a library which simulates true Common Lisp style multiple values. The way this is achieved by having `cl-values' set a global variable which holds the values ONLY IF an "expecting multiple values" flag is set. Any function which supports multiple values must therefore ensure that this flag is suppressed (the term used in the library for this is "sanitization") everywhere in the code except for in the forms which are "returning" (in that, their values is what the function will/may return). While the function author can manually sanitize their function, this is a cumbursome and tedious process so the function `tcl-values--sanitize' is provided to sanitize any forms fed to it, which is called from `tcl-defun' before defining the function. This is the nebulous <processor> that we are talking about. The specific branch in which this eval exists is the basic call to (func arg1 arg2...). It's purpose is - in case `func' is later redefined as a macro - to preserve the sanitization of the function by going through the form and re-sanitize it. This needs to happen at runtime because that's when we are attempting to detect if `func' was redefined or not. Now, I looked into defining a macro which simply called `tcl-values--sanitize', but this led to two problems: 1. This resolution happens at macroexpansion time, due to it being `macroexpand-all'ed. 2. The branch which calls the macro is the same branch we feed into the macro, thus the expansion of the macro contains exactly the same macro, resulting in infinite recursion. There *might* be some trick I can use to get rid of the second problem, but I cannot think of one that would allow me to fix the first one, unless I can somehow prevent macroexpand-all from expanding the resulting macro. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-08 2:07 ` Thuna @ 2024-07-08 2:49 ` Stefan Monnier 2024-07-08 21:38 ` Thuna 0 siblings, 1 reply; 7+ messages in thread From: Stefan Monnier @ 2024-07-08 2:49 UTC (permalink / raw) To: Thuna; +Cc: help-gnu-emacs > All macros are expanded and processed, thus `(,head ,@args) above is > /necessarily/ a function call... that is, unless it is later redefined > as a macro. I think this is the part I don't understand: why do you worry about `head` being a function (or undefined) during macro-expansion but a macro at run-time? If the code is compiled, such a change leads to an error and we blame the change, not the compiler. E.g. M-: (disassemble '(lambda () (foobar 5))) RET turns into the byte-code: byte code: args: nil 0 constant foobar 1 constant 5 2 call 1 3 return and this `call` just signals an error if `foobar` is not a function at run-time. So, I think what I'm saying is that you should probably not try to handle this case at all. Stefan ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-08 2:49 ` Stefan Monnier @ 2024-07-08 21:38 ` Thuna 2024-07-08 22:03 ` Stefan Monnier 0 siblings, 1 reply; 7+ messages in thread From: Thuna @ 2024-07-08 21:38 UTC (permalink / raw) To: Stefan Monnier; +Cc: help-gnu-emacs Aha, I wasn't aware that that situation was considered illegal. Alright, that'll definitely make my job a whole lot easier, thanks for the heads up! ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-08 21:38 ` Thuna @ 2024-07-08 22:03 ` Stefan Monnier 2024-07-08 22:42 ` Thuna 0 siblings, 1 reply; 7+ messages in thread From: Stefan Monnier @ 2024-07-08 22:03 UTC (permalink / raw) To: Thuna; +Cc: help-gnu-emacs > Aha, I wasn't aware that that situation was considered illegal. It's pretty much incompatible with the notion of compilation: Usually, when you compile code like (foo (bar)) under the assumption that `foo` is a function, the code you generate evaluates (bar) before doing anything else. So by the time it looks at `foo` it's too late: if `foo` turns out to be a macro you'd have to "undo" the evaluation of (bar). IOW, you're forced to do something like what you suggested, i.e. replace *every* function call with (if (is-a-macro 'FUN) (handle-the-corner-case 'FUN 'ARGS) (FUN . ARGS)) so the above ends up compiled as if you had written: (if (is-a-macro 'foo) (handle-the-corner-case 'foo '(bar)) (foo (if (is-a-macro 'bar) (handle-the-corner-case 'bar nil) (bar)))) which kills all hopes of generating good-quality code. You can admittedly do better by "watching" the definition of functions and keeping dependencies, so when a function gets redefined as a macro you can throw away all the compiled code which depends on it (and recompile it later as needed). Nevertheless, it's costly and not considered worth the trouble. The reverse is accepted, OTOH: you can take a macro and later turn it into a function. Code compiled when it was defined as a macro will simply keep using the old definition until it gets recompiled. Stefan ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: `eval'ing form in the current lexical environment 2024-07-08 22:03 ` Stefan Monnier @ 2024-07-08 22:42 ` Thuna 0 siblings, 0 replies; 7+ messages in thread From: Thuna @ 2024-07-08 22:42 UTC (permalink / raw) To: Stefan Monnier; +Cc: help-gnu-emacs Yeah, I knew about how macro-redefined-as-function was handled but I (mistakenly) figured the reverse would be handled by expanding it during evaluation. Now, what we are talking about only applies when the code is compiled, so interpreted functions /will/ break, but given I don't have a working fix I am more then willing to call it the programmer's problem and leave it be. ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-07-08 22:42 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-07-07 18:55 `eval'ing form in the current lexical environment Thuna 2024-07-07 19:16 ` Stefan Monnier via Users list for the GNU Emacs text editor 2024-07-08 2:07 ` Thuna 2024-07-08 2:49 ` Stefan Monnier 2024-07-08 21:38 ` Thuna 2024-07-08 22:03 ` Stefan Monnier 2024-07-08 22:42 ` Thuna
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.