One of the exercises in Paul Grahams ANSI Common Lisp book is this: Define a macro that takes a list of variables and a body of code, and ensures that the variables revert to their original values after the body of code is evaluated.
The problem I'm having with this exercise is how to save the symbol-names of the input variables. Below I have a start where I only save the values that the symbols are bound to.
(defmacro save-run (varlist &body body)
`(let ((valuelist (list ,#varlist)))
(format t "valuelist: ~A" valuelist)))
(let ((a 5)(b 6))
(values '(a b))
(save-run (a b)
(setf a 7)
(setf b 8)))
[507]> valuelist: (5 6)
Edit: Here's a solution where the variables are saved and then restored (using tips from finnw below). But shadowing the variables as in Vatine's answer is probably more elegant.
(defmacro save-run (varlist &body body)
`(let ((valuelist (list ,#varlist)))
,#body
(multiple-value-setq ,varlist (values-list valuelist))))
To get the list of symbols, all you need to do is quote the contents of VARLIST:
(defmacro save-run (varlist &body body)
`(let ((namelist ',varlist)
(valuelist (list ,#varlist)))
(format t "namelist: ~A~%" namelist)
(format t "valuelist: ~A~%" valuelist)))
I suspect this will not be useful in the final defnition however. There is not much you can do with the list of symbols at run-time. Instead, look for a good place to insert the list in the macro expansion.
Also you might want to use a GENSYM instead of the hard-coded variable name VALUELIST:
(defmacro save-run (varlist &body body)
(let ((valuelist (gensym)))
`(let ((,valuelist (list ,#varlist)))
,#body
(setf (values ,#varlist) (values-list ,valuelist)))))
Personally, I'd introduce another binding layer for the variables we want to save.
You have the list of variables in varlist so something like this may work:
(defmacro save-run (varlist &body body)
`(let ,(loop for var in varlist
collect (list var var))
,#body))
I really liked Vatine's approach. Here is an implementation that expands to the same code, but uses mapcar instead of the loop macro:
(defmacro save-run (varlist &body body)
`(let ,(mapcar #'list varlist varlist)
,#body))
Related
I'm trying to create a macro (bar) that should be used like this:
(let ((my-var "foo"))
(bar ("some")
:buzz (lambda () (format t "~a~%" my-var))))
The macro should basically just FUNCALL the lambda with taking MY-VAR into account.
What I've come up with is this:
(defmacro bar ((thing) &body body)
`(funcall (coerce (getf (list ,#body) :buzz) 'function)))
This works, it prints "foo".
But I'd like to know if this is how this is done or if there is a better way of doing this.
Well, for a start if you don't need the extra, unused argument, then this is just funcall, since (lambda ...) denotes a function.
(let ((my-var "foo"))
(funcall (lambda () (format t "~a~%" my-var))))
So even if you didn't want to call this funcall you could write it as a function, not a macro: it's not doing any code transformation. But presumably you actually do need this extra argument, and you are intending to use it for some purpose in the expansion, so what you need then is a macro which takes keyword arguments and just expands into a suitable funcall form:
(defmacro bar ((thing) &key (buzz '(lambda ())))
(declare (ignore thing))
`(funcall ,buzz))
I'm learning common lisp. I have written a version of the once-only macro, which suffers from an unusual variable capture problem.
My macro is this:
(defmacro my-once-only (names &body body)
(let ((syms (mapcar #'(lambda (x) (gensym))
names)))
``(let (,,#(mapcar #'(lambda (sym name) ``(,',sym ,,name))
syms names))
,(let (,#(mapcar #'(lambda (name sym) `(,name ',sym))
names syms))
,#body))))
The canonical version of only-once is this:
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,#(loop for g in gensyms collect `(,g (gensym))))
`(let (,,#(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,#(loop for n in names for g in gensyms collect `(,n ,g)))
,#body)))))
The difference, as far as I can tell, is that the canonical version generates new symbols for every expansion of the macro using only-once. For example:
CL-USER> (macroexpand-1 '(once-only (foo) foo))
(LET ((#:G824 (GENSYM)))
`(LET (,`(,#:G824 ,FOO))
,(LET ((FOO #:G824))
FOO)))
T
CL-USER> (macroexpand-1 '(my-once-only (foo) foo))
`(LET (,`(,'#:G825 ,FOO))
,(LET ((FOO '#:G825))
FOO))
T
The variable my macro uses to store the value of foo is the same for every expansion of this form, in this case it would be #:G825. This is akin to defining a macro like the following:
(defmacro identity-except-for-bar (foo)
`(let ((bar 2))
,foo))
This macro captures bar, and this capture manifests when bar is passed to it, like so:
CL-USER> (let ((bar 1))
(identity-except-for-bar bar))
2
However, I cannot think of any way to pass #:G825 to a macro that uses my-only-once so that it breaks like this, because the symbols gensym returns are unique, and I cannot create a second copy of it outside of the macro. I assume that capturing it is unwanted, otherwise the canonical version wouldn't bother adding the additional layer of gensym. How could capturing a symbol like #:G826 be a problem? Please provide an example where this capture manifests.
We can demonstrate a behavioral difference between my-once-only and once-only:
Let's store our test form in a variable.
(defvar *form* '(lexalias a 0 (lexalias b (1+ a) (list a b))))
This test form exercises a macro called lexalias, which we will define in two ways. First with once-only:
(defmacro lexalias (var value &body body)
(once-only (value)
`(symbol-macrolet ((,var ,value))
,#body)))
(eval *form*) -> (0 1)
Then with my-once-only:
(defmacro lexalias (var value &body body)
(my-once-only (value)
`(symbol-macrolet ((,var ,value))
,#body)))
(eval *form*) -> (1 1)
Oops! The problem is that under my-once-only, both a and b end up being symbol-macrolet aliases for exactly the same gensym; the returned expression (list a b) ends up being something like (list #:g0025 #:g0025).
If you're writing a macro-writing helper that implements once-only evaluation, you have no idea how the symbol is going to be used by the code which calls the macro, whose author uses your once-only tool. There are two big unknowns: the nature of the macro and of its use.
As you can see, if you don't make fresh gensyms, it will not work correctly in all conceivable scenarios.
Looking at Practical Common Lisp, we're looking at a simple automated unit test framework. We're trying to write a macro to be used as such:
(check (= (+ 1 2) 3) (= (- 1 4) 9))
This should expand to something using a previously defined function report-result. The suggested implementation is:
(defmacro check (&body forms)
`(progn
,#(loop for f in forms collect `(report-result ,f ',f))))
However, that expansion seems rather procedural to me. I wanted to replace the loop with a mapcar to expand to something like this:
(mapcar #'(lambda (form) (report-result form 'form)) (list form-1 ... form-n))
However, I'm clearly lacking the macro-writing skills to do so. Can someone come up with one such macro?
In case it's relevant, this is the definition of report-result:
(defun report-result (result form)
(format t "~:[FAIL~;pass~] ... ~a~%" result form))
It's indeed fairly simple: you just place the collect expression into the body of your mapcar:
(defmacro check (&body forms)
`(progn
,#(mapcar #'(lambda (form)
`(report-result ,form ',form))
forms)))
You don't really need to know anything about the "macro-y" stuff that's going on, in order to do the replacement you want, which is simply replacing a loop with some other equivalent expression: it will work just as well in a macro context as it would outside.
If you want to expand to a mapcar you can, but there's no real reason to do so, since the list's size is known at compile time. Here's what that would look like:
(defmacro check (&body forms)
`(let ((results (list ,#(mapcar #'(lambda (form)
`(list ,form ',form))
forms))))
(mapcar #'(lambda (result)
(report-result (car result) (cadr result)))
results)))
Which expands like so
> (macroexpand-1 '(check (+ 1 2) (* 2 3)))
(let ((results (list (list (+ 1 2) '(+ 1 2))
(list (* 2 3) '(* 2 3)))))
(mapcar #'(lambda (result) (report-result (car result) (cadr result)))
results))
Which as you can see is rather awkward: the macro already has the forms like (+ 1 2) available to it, but in order to preserve them to runtime for the mapcar lambda to see, you have to emit the input form twice. And you have to produce the whole list to map over, rather than just producing a list that's "finished" to begin with. Additionally, this produces a list as output, and requires having all the inputs and outputs in memory at once: the original macro with progn produced the inputs and outputs one at a time, and discarded them when finished.
In Python, I am able to use yield to build up a list without having to define a temporary variable:
def get_chars_skipping_bar(word):
while word:
# Imperative logic which can't be
# replaced with a for loop.
if word[:3] == 'bar':
word = word[3:]
else:
yield foo[0]
foo = foo[1:]
In elisp, I can't see any way of doing this, either built-in or using any pre-existing libraries. I'm forced to manually build a up a list and call nreverse on it. Since this is a common pattern, I've written my own macro:
(require 'dash)
(require 'cl)
(defun replace-calls (form x func)
"Replace all calls to X (a symbol) in FORM,
calling FUNC to generate the replacement."
(--map
(cond
((consp it)
(if (eq (car it) x)
(funcall func it)
(replace-calls it x func)))
(:else it))
form))
(defmacro with-results (&rest body)
"Execute BODY, which may contain forms (yield foo).
Return a list built up from all the values passed to yield."
(let ((results (gensym "results")))
`(let ((,results (list)))
,#(replace-calls body 'yield
(lambda (form) `(push ,(second form) ,results)))
(nreverse ,results))))
Example usage:
(setq foo "barbazbarbarbiz")
(with-results
(while (not (s-equals? "" foo))
;; Imperative logic which can't be replaced with cl-loop's across.
(if (s-starts-with? "bar" foo)
(setq foo (substring foo 3))
(progn
(yield (substring foo 0 1))
(setq foo (substring foo 1))))))
There must be a better way of doing this, or an existing solution, somewhere in elisp, cl.el, or a library.
The Python function is actually a generator. In ANSI Common Lisp, we would usually reach for a lexical closure to simulate a generator, or else us a library to define generators directly, like Pygen. Maybe these approaches can be ported to Emacs Lisp.
AFAIK, people just use push+nreverse like you do. If you want to define your macro in a more robust way (e.g. so it doesn't misfire on something like (memq sym '(yield stop next))) you could do it as:
(defmacro with-results (&rest body)
"Execute BODY, which may contain forms (yield EXP).
Return a list built up from all the values passed to `yield'."
(let ((results (gensym "results")))
`(let ((,results '()))
(cl-macrolet ((yield (exp) `(push ,exp ,results)))
,#body)
(nreverse ,results))))
Maybe something like this:
(setq foo "barbaz")
(cl-loop for i from 0 to (1- (length foo))
collect (string (aref foo i)))
In any case, there's nothing wrong with push and nreverse.
Lisp is different from Python. yield is not used. I also see the use of coroutine-like constructs for this as a mistake. It's the equivalent of the come-from construct. Suddenly routines have multiple context dependent entry points.
In Lisp use functions/closures instead.
In Common Lisp, the LOOP macro allows efficient mappings over vectors. The following code can be abstracted to some mapping function, if preferred:
CL-USER 17 > (defun chars-without-substring (string substring)
(loop with i = 0
while (< i (length string))
when (and (>= (- (length string) i) (length substring))
(string= substring string
:start2 i
:end2 (+ i (length substring))))
do (incf i (length substring))
else
collect (prog1 (char string i) (incf i))))
CHARS-WITHOUT-SUBSTRING
CL-USER 18 > (chars-without-substring "barbazbarbarbiz" "bar")
(#\b #\a #\z #\b #\i #\z)
I'm writing a small special-purpose language in Common Lisp using a defmacro. I cannot figure out the proper backquote procedure to have a variable defined in a top-level let statement, shadowed in a nested macrolet, and returned in a nested labels, all point to the same symbol.
Here's some small example code that shows the problem:
(defmacro with-keylang (&body body)
(let ((keyblank-size-x (gensym)))
`(let ((,keyblank-size-x 42))
(labels ((keyblank-size-x ()
,keyblank-size-x))
(macrolet ((with-keyblank-size-x (size-x &body body)
`(let ((,',keyblank-size-x ,size-x))
,#body)))
,#body)))))
CL-USER>
(with-keylang
(print (keyblank-size-x)))
42
42
All is well so far.
CL-USER>
(with-keylang
(with-keyblank-size-x 24
(print (keyblank-size-x))))
;Compiler warnings :
; In an anonymous lambda form: Unused lexical variable #:G123171
42
42
There's the problem. I want the symbol representing keyblank-size-x to be shadowed with the value 24, and this is not happening.
I have a feeling that the ,', backquote pattern isn't proper for this case, as this quotes the symbol representing keyblank-size-x, and is therefore not eq. But if I try ,,, it doesn't work, and I get this interesting compiler error:
While compiling WITH-KEYBLANK-SIZE-X :
Illegal reference to lexically defined variable #:G123192.
[Condition of type CCL::COMPILE-TIME-PROGRAM-ERROR]
EDIT:
The keyblank-size-x variable was lexically scoped, and I wanted dynamic scope for this particular case. So here's the rewrite declaring the keyblank-size-x variable to have dynamic scope:
(defmacro with-keylang (&body body)
(let ((keyblank-size-x (gensym)))
`(let ((,keyblank-size-x 42))
(declare (special ,keyblank-size-x))
(labels ((keyblank-size-x ()
,keyblank-size-x))
(macrolet ((with-keyblank-size-x (size-x &body body)
`(let ((,',keyblank-size-x ,size-x))
(declare (special ,',keyblank-size-x))
,#body)))
,#body)))))
And the test code:
CL-USER>
(with-keylang
(with-keyblank-size-x 25
(with-keyblank-size-x 21
(print (keyblank-size-x)))
(print (keyblank-size-x)))
(print (keyblank-size-x)))
21
25
42
42
If we fully expand the code (here using the Walk command in LispWorks):
(LET ((#:G19508 42))
(LABELS ((KEYBLANK-SIZE-X () #:G19508))
(MACROLET ((WITH-KEYBLANK-SIZE-X (SIZE-X &BODY BODY)
`(LET ((#:G19508 ,SIZE-X))
,#BODY)))
(LET ((#:G19508 24))
(PRINT (KEYBLANK-SIZE-X))))))
Rebinding #:G19508 has no effect and can't have. The function keyblank-size-x has a different lexical binding. This is just the usual effect of lexical binding.