The code below implements a macro call with an implicit optional parameter--implicit because it is buried in the &rest parameter. Is there a better way to code the macro (and its supporting function)--perhaps by making the optional parameter explicit with an &optional keyword (or maybe some other way)? It is preferred that the first parameter is required, the second optional, and the numerous remaining parameters required. Also prefer to keep the macro definition simple, with the work done by the supporting function, if possible (but looking to learn more advanced approaches too):
(defstruct action
name duration slot1 slot2 slot3)
(defmacro define-action (name &rest rest)
`(install-action ',name ',rest))
(defun install-action (name &rest rest)
(let* ((slots (first rest))
(duration (if (numberp (first slots))
(prog1 (first slots)
(setf slots (cdr slots)))
0))
(action (make-action :name name :duration duration
:slot1 (first slots) :slot2 (second slots)
:slot3 (third slots))))
(print action)))
(define-action move a b c) ;first example no duration
(define-action move 1 a b c) ;second example with duration=1
#S(ACTION :NAME MOVE :DURATION 0 :SLOT1 A :SLOT2 B :SLOT3 C)
#S(ACTION :NAME MOVE :DURATION 1 :SLOT1 A :SLOT2 B :SLOT3 C)
Additional point of clarification: The slot values above are really various specifications represented as (sometimes deeply nested) lisp trees. The function install-action interprets the specs and installs their semantic content into a database.
Parameter and argument lists: style
It is useful to have explicit parameter lists. Common Lisp provides extensive support for it. But even then not every parameter list idea can be supported. As jkiiski notes in his comment, it is always helpful to have a speaking parameter list: it helps the developer, the compiler can catch some errors at compile time and Lisp can provide better debugging information.
One of the style rules: optional arguments should be at the end of the argument list. Common Lisp itself violates this at least one place (can only remember one function right now) and it is always painful and error prone.
Fix the arglist of INSTALL-ACTION:
(defun install-action (name slots &optional (duration 0))
...)
Fix the arglist of the macro, too
Use it like:
(define-action move (a b c) 1)
A list of things might better be grouped in the macro interface.
(defmacro define-action (name slots &optional duration)
...)
Or even use a keyword named arguments:
(define-action move :slots (a b c) :duration 1)
It gets longer, but readability is greatly improved.
Side question: do we need a macro DEFINE-ACTION and why?
The main reasons for such a macro are usually:
less quoting
special syntax
compile-time side-effects
expansion into other macro calls
Related
In Common Lisp, a macro definition must have been seen before the first use. This allows a macro to refer to itself, but does not allow two macros to refer to each other. The restriction is slightly awkward, but understandable; it makes the macro system quite a bit easier to implement, and to understand how the implementation works.
Is there any Lisp family language in which two macros can refer to each other?
What is a macro?
A macro is just a function which is called on code rather than data.
E.g., when you write
(defmacro report (x)
(let ((var (gensym "REPORT-")))
`(let ((,var ,x))
(format t "~&~S=<~S>~%" ',x ,var)
,var)))
you are actually defining a function which looks something like
(defun macro-report (system::<macro-form> system::<env-arg>)
(declare (cons system::<macro-form>))
(declare (ignore system::<env-arg>))
(if (not (system::list-length-in-bounds-p system::<macro-form> 2 2 nil))
(system::macro-call-error system::<macro-form>)
(let* ((x (cadr system::<macro-form>)))
(block report
(let ((var (gensym "REPORT-")))
`(let ((,var ,x)) (format t "~&~s=<~s>~%" ',x ,var) ,var))))))
I.e., when you write, say,
(report (! 12))
lisp actually passes the form (! 12) as the 1st argument to macro-report which transforms it into:
(LET ((#:REPORT-2836 (! 12)))
(FORMAT T "~&~S=<~S>~%" '(! 12) #:REPORT-2836)
#:REPORT-2836)
and only then evaluates it to print (! 12)=<479001600> and return 479001600.
Recursion in macros
There is a difference whether a macro calls itself in implementation or in expansion.
E.g., a possible implementation of the macro and is:
(defmacro my-and (&rest args)
(cond ((null args) T)
((null (cdr args)) (car args))
(t
`(if ,(car args)
(my-and ,#(cdr args))
nil))))
Note that it may expand into itself:
(macroexpand '(my-and x y z))
==> (IF X (MY-AND Y Z) NIL) ; T
As you can see, the macroexpansion contains the macro being defined.
This is not a problem, e.g., (my-and 1 2 3) correctly evaluates to 3.
However, if we try to implement a macro using itself, e.g.,
(defmacro bad-macro (code)
(1+ (bad-macro code)))
you will get an error (a stack overflow or undefined function or ...) when you try to use it, depending on the implementation.
Here's why mutually recursive macros can't work in any useful way.
Consider what a system which wants to evaluate (or compile) Lisp code for a slightly simpler Lisp than CL (so I'm avoiding some of the subtleties that happen in CL), such as the definition of a function, needs to do. It has a very small number of things it knows how to do:
it knows how to call functions;
it knows how to evaluate a few sorts of literal objects;
it has some special rules for a few sorts of forms – what CL calls 'special forms', which (again in CL-speak) are forms whose car is a special operator;
finally it knows how to look to see whether forms correspond to functions which it can call to transform the code it is trying to evaluate or compile – some of these functions are predefined but additional ones can be defined.
So the way the evaluator works is by walking over the thing it needs to evaluate looking for these source-code-transforming things, aka macros (the last case), calling their functions and then recursing on the results until it ends up with code which has none left. What's left should consist only of instances of the first three cases, which it then knows how to deal with.
So now think about what the evaluator has to do if it is evaluating the definition of the function corresponding to a macro, called a. In Cl-speak it is evaluating or compiling a's macro function (which you can get at via (macro-function 'a) in CL). Let's assume that at some point there is a form (b ...) in this code, and that b is known also to correspond to a macro.
So at some point it comes to (b ...), and it knows that in order to do this it needs to call b's macro function. It binds suitable arguments and now it needs to evaluate the definition of the body of that function ...
... and when it does this it comes across an expression like (a ...). What should it do? It needs to call a's macro function, but it can't, because it doesn't yet know what it is, because it's in the middle of working that out: it could start trying to work it out again, but this is just a loop: it's not going to get anywhere where it hasn't already been.
Well, there's a horrible trick you could do to avoid this. The infinite regress above happens because the evaluator is trying to expand all of the macros ahead of time, and so there's no base to the recursion. But let's assume that the definition of a's macro function has code which looks like this:
(if <something>
(b ...)
<something not involving b>)
Rather than doing the expand-all-the-macros-first trick, what you could do is to expand only the macros you need, just before you need their results. And if <something> turned out always to be false, then you never need to expand (b ...), so you never get into this vicious loop: the recursion bottoms out.
But this means you must always expand macros on demand: you can never do it ahead of time, and because macros expand to source code you can never compile. In other words a strategy like this is not compatible with compilation. It also means that if <something> ever turns out to be true then you'll end up in the infinite regress again.
Note that this is completely different to macros which expand to code which involves the same macro, or another macro which expands into code which uses it. Here's a definition of a macro called et which does that (it doesn't need to do this of course, this is just to see it happen):
(defmacro et (&rest forms)
(if (null forms)
't
`(et1 ,(first forms) ,(rest forms))))
(defmacro et1 (form more)
(let ((rn (make-symbol "R")))
`(let ((,rn ,form))
(if ,rn
,rn
(et ,#more)))))
Now (et a b c) expands to (et1 a (b c)) which expands to (let ((#:r a)) (if #:r #:r (et b c))) (where all the uninterned things are the same thing) and so on until you get
(let ((#:r a))
(if #:r
#:r
(let ((#:r b))
(if #:r
#:r
(let ((#:r c))
(if #:r
#:r
t))))))
Where now not all the uninterned symbols are the same
And with a plausible macro for let (let is in fact a special operator in CL) this can get turned even further into
((lambda (#:r)
(if #:r
#:r
((lambda (#:r)
(if #:r
#:r
((lambda (#:r)
(if #:r
#:r
t))
c)))
b)))
a)
And this is an example of 'things the system knows how to deal with': all that's left here is variables, lambda, a primitive conditional and function calls.
One of the nice things about CL is that, although there is a lot of useful sugar, you can still poke around in the guts of things if you like. And in particular, you still see that macros are just functions that transform source code. The following does exactly what the defmacro versions do (not quite: defmacro does the necessary cleverness to make sure the macros are available early enough: I'd need to use eval-when to do that with the below):
(setf (macro-function 'et)
(lambda (expression environment)
(declare (ignore environment))
(let ((forms (rest expression)))
(if (null forms)
't
`(et1 ,(first forms) ,(rest forms))))))
(setf (macro-function 'et1)
(lambda (expression environment)
(declare (ignore environment))
(destructuring-bind (_ form more) expression
(declare (ignore _))
(let ((rn (make-symbol "R")))
`(let ((,rn ,form))
(if ,rn
,rn
(et ,#more)))))))
There have been historic Lisp systems that allow this, at least in interpreted code.
We can allow a macro to use itself for its own definition, or two or more macros to mutually use each other, if we follow an extremely late expansion strategy.
That is to say, our macro system expands a macro call just before it is evaluated (and does that each time that same expression is evaluated).
(Such a macro expansion strategy is good for interactive development with macros. If you fix a buggy macro, then all code depending on it automatically benefits from the change, without having to be re-processed in any way.)
Under such a macro system, suppose we have a conditional like this:
(if (condition)
(macro1 ...)
(macro2 ...))
When (condition) is evaluated, then if it yields true, (macro1 ...) is evaluated, otherwise (macro2 ...). But evaluation also means expansion. Thus only one of these two macros is expanded.
This is the key to why mutual references among macros can work: we are able rely on the conditional logic to give us not only conditional evaluation, but conditional expansion also, which then allows the recursion to have ways of terminating.
For example, suppose macro A's body of code is defined with the help of macro B, and vice versa. And when a particular invocation of A is executed, it happens to hit the particular case that requires B, and so that B call is expanded by invocation of macro B. B also hits the code case that depends on A, and so it recurses into A to obtain the needed expansion. But, this time, A is called in a way that avoids requiring, again, an expansion of B; it avoids evaluating any sub-expression containing the B macro. Thus, it calculates the expansion, and returns it to B, which then calculates its expansion returns to the outermost A. A finally expands and the recursion terminates; all is well.
What blocks macros from using each other is the unconditional expansion strategy: the strategy of fully expanding entire top-level forms after they are read, so that the definitions of functions and macros contain only expanded code. In that situation there is no possibility of conditional expansion that would allow for the recursion to terminate.
Note, by the way, that a macro system which expands late doesn't recursively expand macros in a macro expansion. Suppose (mac1 x y) expands into (if x (mac2 y) (mac3 y)). Well, that's all the expansion that is done for now: the if that pops out is not a macro, so expansion stops, and evaluation proceeds. If x yields true, then mac2 is expanded, and mac3 is not.
I am trying to write a super-tiny Object-oriented system with syntax-rules, mostly just to learn it. Anyway, I am trying to introduce a "this" variable. Here is what I would like to be able to do:
(oo-class Counter
(
(attr value 0)
(attr skip 1)
)
(
(method (next) (set! value (+ value skip)) value)
(method (nextnext) (this 'next) (this 'next))
(method (set-value newval) (set! value newval))
(method (set-skip newskip) (set! skip newskip))
)
)
(define c (Counter))
((c 'set-value) 23)
((c 'next))
((c 'nextnext))
I can get everything to work except "this". It seems like syntax-rules doesn't allow variable introduction. I thought I could get it by defining it as one of the literals in syntax-rules, but this does not seem to work.
Below is my object-oriented system:
(define-syntax oo-class
(syntax-rules (attr method this)
(
(oo-class class-name
((attr attr-name initial-val) ...)
((method (meth-name meth-arg ...) body ...) ...))
(define class-name
(lambda ()
(letrec
(
(this #f)
(attr-name initial-val)
...
(funcmap
(list
(cons (quote meth-name) (cons (lambda (meth-arg ...) body ...) '()))
...
)
)
)
(set! this (lambda (methname)
(cadr (assoc methname funcmap))
))
this
)
)
)
)
)
)
This works for everything except 'nextnext, which errors out when it tries to reference "this".
Is this the right way to do this? Is there some other way to do this? I recognize that this is slightly unhygienic, but isn't that at least part of the point of specifying literals?
I've tried this in Chicken Scheme as well as DrRacket in R5RS mode (other modes get complainy about "this").
Below is the whole file. You can run it on Chicken with just "csi object.scm"
https://gist.github.com/johnnyb/211e105882248e892fa485327039cc90
I also tried to use let-syntax and use (this) as a syntax specifier to refer to the (this) variable. But, as far as I could tell, it wasn't letting me directly access a variable of my own making within the syntax rewriting.
BONUS QUESTION: What is an easy way to see the result of a syntax-rules transformation for debugging? Is there some way to get chicken (or something else) to do the transformation and spit out the result? I tried some stuff on DrRacket, but it doesn't work in R5RS mode.
I recognize that this is slightly unhygienic, but isn't that at least part of the point of specifying literals?
No, the literals exist so you can match literally on keywords, like for example the => or the else in a cond clause. It's still hygienic because if => or else is lexically bound to some value, that has precedence:
(let ((else #f))
(cond (else (display "hi!\n")))) ;; Will not print
Now, you could write a very tedious macro that matches this at any possible place and nesting level in the expansion, but that will never be complete, and it would not nest lexically, either.
It is possible to do what you're trying to do using what has become known as Petrofsky extraction, but it's a total and utter hack and abuse of syntax-rules and it does not work (consistently) in the presence of modules across implementations (for example, exactly in CHICKEN we've had a complaint that we accidentally "broke" this feature).
What I'd suggest is writing a syntax-rules macro that accepts an identifier in its input which will be bound to the current object, then write one trivial unhygienic macro that calls that other macro with the hardcoded identifier this as input.
What is an easy way to see the result of a syntax-rules transformation for debugging? Is there some way to get chicken (or something else) to do the transformation and spit out the result? I tried some stuff on DrRacket, but it doesn't work in R5RS mode.
In csi, you can use ,x (macro-call), but it will only do one level of expansion.
A common trick that works in every Scheme implementation is to change your macro definition to quote its output. So, it expands not to (foo) but to '(foo). That way, you can just call the macro in the REPL and see its result immediately.
Q: is it possible to write a Common Lisp function that accepts the same arguments either by position or by keyword? If so, how?
For example, how could one rewrite the following function:
(defun fnx (&key a b c)
(list a b c))
Such that one could legally call it in each of the following ways:
(fnx 1 2 3)
(fnx :a 1 :b 2 :c 3)
(fnx 1 2 :c 3)
Yes you can, but, no, you don't want to.
More precisely, if you can write the spec, you can implement it in Lisp, but I don't think you can write an unambiguous spec. E.g., what should these invocations return:
(fnx :a :b :c)
(fnx :a :c :b)
(fnx :b :a :a)
(fnx :a :a :a)
and many other corner cases.
You can, of course, write something like this:
(defun parse-my-args (&rest args)
(let (a b c (curpos 0))
... some hairy processing
(values a b c)))
(defun fnx (&rest args)
(multiple-values-bind (a b c) (parse-my-args args)
...))
and declare that whatever the code is doing is the right thing, but this does not look like a good idea.
I suggest you think what you want to accomplish and ask a question about that.
In addition to sds's answer, I would recommend you reading this chapter of 'Practical Common Lisp'.
Author of the book describes how one can combine different types of function arguments and which combinations should be avoided.
Here is relevant citation:
The other two combinations, either &optional or &rest parameters combined with &key parameters, can lead to somewhat surprising behavior. <...> You can safely combine &rest and &key parameters, but the behavior may be a bit surprising initially. Normally the presence of either &rest or &key in a parameter list causes all the values remaining after the required and &optional parameters have been filled in to be processed in a particular way--either gathered into a list for a &rest parameter or assigned to the appropriate &key parameters based on the keywords. If both &rest and &key appear in a parameter list, then both things happen--all the remaining values, which include the keywords themselves, are gathered into a list that's bound to the &rest parameter, and the appropriate values are also bound to the &key parameters.
So using of &rest (which is how you can 'index' parameters of a function) excludes possibility of using &key.
Of course, one can do any kind of insane mess with such a crazy tool as Lisp, but in this particular case it is not worth it.
I have a program that takes as inputs a chunk of data and a list of rules, applying both a set of standard rules and the rules given as input to the chunk of data. The size of both inputs may vary.
I want to be able to write a list of rules like this:
(rule-generating-macro
(rule-1-name rule-1-target
(rule-action-macro (progn actions more-actions)))
(rule-2-name rule-2-target
(rule-action-macro (or (action-2) (default-action))))
;; more rules
)
Right now, rules are more verbose -- they look more like
(defvar rule-list
`((rule-1-name rule-1-target
,#(rule-action-macro (progn actions more-actions)))
(rule-2-name rule-2-target
,#(rule-action-macro (or (action-2) (default-action))))
;; more rules
)
The latter form looks uglier to me, but I can't figure out how to write a macro that can handle a variable-length &rest argument, iterate over it, and return the transformed structure. Using a defun instead of a defmacro isn't really on the table because (as hopefully the example shows) I'm trying to control evaluation of the list of rules instead of evaluating the list when my program first sees it, and once you need to control evaluation, you're in defmacro territory. In this case, the thorny point is the rule-action-macro part - getting the interpreter to read that and use its expanded value has been problematic.
How can I create a macro that handles a variable-length argument so that I can write rule lists in a concise way?
defmacro will happily accept a &rest argument
(see Defining Macros for Emacs Lisp and Macro Lambda Lists for Common Lisp).
Then you can do pretty much anything you want with it in the macro body - e.g., iterate over it. Remember, macro is much more than just backquote!
E.g.:
(defmacro multidefvar (&rest vars)
(let ((forms (mapcar (lambda (var) `(defvar ,var)) vars)))
`(progn ,#forms)))
(macroexpand '(multidefvar a b c d))
==> (PROGN (DEFVAR A) (DEFVAR B) (DEFVAR C) (DEFVAR D))
I am building a mechanism to take an arbitrary CLOS object and return a hash from it (useful in my debugging experience).
However, I am not sure how to force a variable expansion. I sense that the solution lies with a correct use of gensym, but I'm not sure how.
;;helper macro
(defun class-slots-symbols (class-name)
"Returns a list of the symbols used in the class slots"
(mapcar 'closer-mop:slot-definition-name
(closer-mop:class-slots
(find-class class-name))))
;;macro that I am having difficulty with
(defmacro obj-to-hash (obj-inst)
"Reads an object, reflects over its slots, and returns a hash table of them"
`(let ((new-hash (make-hash-table))
(slot-list (class-slots-symbols (type-of ,obj-inst))))
;;The slot-list needs to expand out correctly in the with-slots form
(with-slots (slot-list) obj-inst
(loop for slot in slot-list do ;and also here
(format t "~a~&" slot)
(hashset new-hash (string slot) slot)))))
After a macroexpand-1, I find that that this expands into the following code (*bar* is a class object):
(macroexpand-1 '(obj-to-hash *bar*))
LET ((NEW-HASH (MAKE-HASH-TABLE))
(SLOT-LIST (CLASS-SLOTS-SYMBOLS (TYPE-OF *BAR*))))
(WITH-SLOTS (SLOT-LIST) ;; <-- this needs to be expanded to *bar*'s slots
*BAR*
(LOOP FOR SLOT IN SLOT-LIST ;;<-- not so important
DO (FORMAT T "~a~&" SLOT) (HASHSET NEW-HASH (STRING SLOT) SLOT))))
Obviously, the problem is that slot-list is not being expanded. Less obvious (to me) is the solution.
Followup: After Rainer pointed me in the right direction:
(defun class-slots-symbols (class-instance)
"Returns a list of the symbols used in the class slots"
(mapcar 'closer-mop:slot-definition-name
(closer-mop:class-slots
(class-of class-instance))))
(defun object-to-hash (obj)
"Reflects over the slots of `obj`, and returns a hash table mapping
slots to their values"
(let ((new-hash (make-hash-table))
(slot-list (class-slots-symbols obj)))
(loop for slot in slot-list do
(hashset new-hash (string slot)
(slot-value obj slot)))
new-hash))
Just looking at it I can see no reason why this should be a macro. Rewriting it as a function will save you a lot of trouble.
The use of WITH-SLOTS is not possible they way you try it. The object is not known in general until runtime. The compiler needs to know the slots of the object at compile time already. You need to use SLOT-VALUE and look up the slot value at runtime.
You are thinking in many ways too complicated and your code is slightly confused. You can get rid of some confusion by following simple rules and avoiding some wording.
Let's look at your code:
First, it is not a helper macro, since what follows is a function.
;;helper macro
(defun class-slots-symbols (class-name)
Why take a class name? Why not use the class itself? Classes are first class objects. Write function with obvious interfaces. Elementary functions should work on the basic data types.
"Returns a list of the symbols used in the class slots"
In the class slots no symbols are used. slots have names, one can get this symbol.
(mapcar 'closer-mop:slot-definition-name
(closer-mop:class-slots
(find-class class-name))))
It is no wonder you have a problem with this macro. It is simply because it should be a function, not a macro. Macros are for source transformation. All you need is a simple computation, so no macro is needed
;;macro that I am having difficulty with
(defmacro obj-to-hash (obj-inst)
Poor wording: obj-inst. Either name it object or instance. Not both.
"Reads an object, reflects over its slots, and returns a hash table of them"
Poor documentation: you don't READ anything. Read is an I/O operation and in your code is none. You are talking about an 'object', but above you have something like 'obj-inst'. Why talk about the same thing in two different ways? You may want to document what the hash table actual maps. From which keys to which values?
`(let ((new-hash (make-hash-table))
new-hash is also a poor name. Basically the thing is a hash-table.
(slot-list (class-slots-symbols (type-of ,obj-inst))))
Why TYPE-OF and then later in the helper function call FIND-CLASS? Common Lisp has CLASS-OF, which returns the class directly.
;;The slot-list needs to expand out correctly in the with-slots form
(with-slots (slot-list) obj-inst
Above won't work since WITH-SLOTS expects slot names at compile time, not a slot-list.
(loop for slot in slot-list do ;and also here
(format t "~a~&" slot)
(hashset new-hash (string slot) slot)
HASHSET is not needed, unless it does something special. The usual way to set values is via SETF. SETF takes the form to read a place and the form to compute a value. That's all. It works for all kinds of data structures. One never needs to remember again how the writer function looks like (name, parameter list, ...).
))))
Here is my version:
Note that I use the package CLOS, you may want to use your package CLOSER-MOP
(defun class-slots-symbols (class)
"Returns a list of the symbol names of the class slots"
(mapcar 'clos:slot-definition-name
(clos:class-slots class)))
Above is a simple function taking a class and returning the list of slot names.
Next, we have a simple function, which in this form has been written a million times in Common Lisp:
(defun object-to-hash (object)
"returns a hashtable with the object's slots as keys and slot-values as values"
(let ((hash-table (make-hash-table)))
(loop for slot-name in (class-slots-symbols (class-of object))
do (setf (gethash slot-name hash-table)
(string (slot-value object slot-name))))
hash-table))
We can also rewrite it to slightly older style Lisp:
(defun object-to-hash (object &aux (hash-table (make-hash-table)))
"returns a hashtable with the object's slots as keys
and string versions of the slot-values as values"
(dolist (slot-name (class-slots-symbols (class-of object)) hash-table)
(setf (gethash slot-name hash-table)
(string (slot-value object slot-name)))))
Above is much simpler and has the whole confusion about macros, generating code, compile time information vs. runtime, ... removed. It is much easier to understand, maintain and debug.