I've got another question involving self-reference in Common Lisp. I found a thread on Stack Exchange which poses a problem of writing the shortest program that would print all printable ASCII characters NOT present in the program's source code. This got me thinking how to tackle the problem in Common Lisp. I hit against two problems - one probably trivial, the other more tricky:
First is the case of writing a CL script, e.g. starting with #!/usr/bin/env sbcl --script. I thought that through *posix-argv* I could access all command line arguments including the name of the called script. I also looked for the equivalent of Bash $0 but could find none. What worked for me in the end is this ugly Bash-ified SBCL script, which explicitly passes $0 to SBCL and proceeds from that:
#!/bin/bash
#|
sbcl --script $0 $0
exit
|#
(defun file-string (path)
(with-open-file (stream path)
(let ((data (make-string (file-length stream))))
(read-sequence data stream)
data)))
(let* ((printable (mapcar #'code-char (loop for i from #x20 to #x7e collect i)))
(absent (set-difference
printable
(coerce (file-string (cadr *posix-argv*)) 'list))))
(print (coerce absent 'string)))
My question regarding this point is: can you think of any way of doing it without relying so heavily on Bash supplying relevant arguments? Or, more briefly: is there a CL (SBCL in particular) equivalent of $0?
Now comes the part that I'm totally puzzled with. Before resorting to the script approach above I tried to accomplish this goal in a more REPL-oriented way. Based on the &whole specifier in defmacro and considerations in this thread I've tried to get the name of the macro from the &whole argument and somehow "read in" its source. And I have absolutely no idea how to do it. So in short: given the name of the macro, can I somehow obtain the defmacro form which defined it? And I'm talking about a generic solution, rather than parsing the REPL history.
EDIT: Regarding mbratch's question about use of macroexpand-1 here's how I do it:
(defmacro self-refer (&whole body)
(macroexpand-1 `',body))
With this call I'm able to obtain (SELF-REFER) by calling (SELF-REFER). Which isn't much of a solution...
I hope someone could point me in the right direction. Thanks!
Getting the source of a macro is not defined in Common Lisp.
This may work (Example from LispWorks):
CL-USER 10 > (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO
CL-USER 11 > (pprint (function-lambda-expression (macro-function 'foo)))
(LAMBDA
(DSPEC::%%MACROARG%% #:&ENVIRONMENT1106 &AUX (#:&WHOLE1107 DSPEC::%%MACROARG%%)
(#:\(A\ ...\)1108 (CDR #:&WHOLE1107))
(#:CHECK-LAMBDA-LIST-TOP-LEVEL1110
(DSPEC::CHECK-LAMBDA-LIST-TOP-LEVEL '(A B)
#:&WHOLE1107
#:\(A\ ...\)1108
2
2
'NIL
:MACRO))
(A (CAR (DSPEC::THE-CONS #:\(A\ ...\)1108)))
(#:\(B\)1109 (CDR (DSPEC::THE-CONS #:\(A\ ...\)1108)))
(B (CAR (DSPEC::THE-CONS #:\(B\)1109))))
(DECLARE (LAMBDA-LIST A B))
(BLOCK FOO `(* (+ ,A ,B) (+ ,A ,A))))
An even more esoteric way is to alter the existing DEFMACRO to record its source.
Many Lisp implementations have a non-standard feature called advising. LispWorks for example can advise macros:
CL-USER 31 > (defadvice (defmacro source-record-defmacro :after)
(&rest args)
(setf (get (second (first args)) :macro-source) (first args)))
T
Above adds code to the standard DEFMACRO macro, which records the source on the symbol property list of the macro name. defmacro is the name of the thing to advise. source-record-defmacro is the chosen name of this advice. :after then specifies that the code should run after the normal defmacro code.
CL-USER 32 > (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO
CL-USER 33 > (pprint (get 'foo :macro-source))
(DEFMACRO FOO (A B) `(* (+ ,A ,B) (+ ,A ,A)))
Again, this is completely non-standard - I'm not sure if a comparable mechanism exists for SBCL, though it has something called 'encapsulate'.
A very belated followup to Rainer Joswig's LispWorks solution. I've been using Allegro CL lately and discovered the fwrap facility. Conceptually it's very similar to the defadvice above and slighly more verbose. Here's a re-iteration of Rainer's example in ACL 10.0:
(def-fwrapper source-record-defmacro (&rest args)
(setf (get (second (first args)) :macro-source) (first args))
(call-next-fwrapper))
Having defined an fwrapper you need to "put it into action" explicitly:
(fwrap 'defmacro 'srd 'source-record-defmacro)
After this it's like in Rainer's example:
CL-USER> (defmacro foo (a b) `(* (+ ,a ,b) (+ ,a ,a)))
FOO
CL-USER> (pprint (get 'foo :macro-source))
(DEFMACRO FOO (A B) `(* (+ ,A ,B) (+ ,A ,A)))
; No value
Related
Given the macro:
(defclass sample-class ()
((slot-1 :accessor slot-1
:initform "sample slot")))
(defvar *sample-instance*(make-instance 'sample-class))
(defmacro sample-macro (p)
`(if (typep ,p 'sample-class)
(progn
(print "evaluated")
(print ,(slot-1 p)))))
(sample-macro *sample-instance*)
I am confused as to why this is the error output
Execution of a form compiled with errors.
Form:
(SAMPLE-MACRO *SAMPLE-INSTANCE*)
Compile-time error:
(during macroexpansion of (SAMPLE-MACRO *SAMPLE-INSTANCE*))
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
(*SAMPLE-INSTANCE*).
See also:
The ANSI Standard, Section 7.6.6
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
Shouldn't the macro expand and evaluate the s-form in the process? Why is the reader not finding the generic function slot-1?
I think you are confused about what macros do. Macros are transformations of source code. So consider what happens when the system tries to expand the macro form (sample-macro *sample-instance*). At macroexpansion time, p is the symbol *sample-instance*: a representation of a bit of source code.
So now, look at the backquoted form in the body of the macro: in it there is ,(slot-1 p): this will try and call slot-1 on whatever p is bound to, which is a symbol. This then fails and the macroexpansion fails as a result.
Well, you could 'fix' this in a way which seems obvious:
(defmacro sample-macro (p)
`(if (typep ,p 'sample-class)
(progn
(print "evaluated")
(print (slot-1 ,p)))))
And this seems to work. Using a macroexpansion tracer:
(sample-macro *sample-instance*)
-> (if (typep *sample-instance* 'sample-class)
(progn (print "evaluated") (print (slot-1 *sample-instance*))))
And if you use the macro it will 'work'. Except it won't work, at all: Consider this form: (sample-macro (make-instance 'sample-class)): well, let's look at that using the macro tracer:
(sample-macro (make-instance 'sample-class))
-> (if (typep (make-instance 'sample-class) 'sample-class)
(progn
(print "evaluated")
(print (slot-1 (make-instance 'sample-class)))))
Oh dear.
So we could work around this problem by rewriting the macro like this:
(defmacro sample-macro (p)
`(let ((it ,p))
(if (typep it 'sample-class)
(progn
(print "evaluated")
(print (slot-1 it)))
And now
(sample-macro (make-instance 'sample-class))
-> (let ((it (make-instance 'sample-class)))
(if (typep it 'sample-class)
(progn (print "evaluated") (print (slot-1 it)))))
Which is better. And in this case it's even safe, but in the great majority of cases we'd need to use a gensym for the thing I've called it:
(defmacro sample-macro (p)
(let ((itn (make-symbol "IT"))) ;not needed for this macro
`(let ((,itn ,p))
(if (typep ,itn 'sample-class)
(progn
(print "evaluated")
(print (slot-1 ,itn)))))))
And now:
(sample-macro (make-instance 'sample-class))
-> (let ((#:it (make-instance 'sample-class)))
(if (typep #:it 'sample-class)
(progn (print "evaluated") (print (slot-1 #:it)))))
So this (and in fact the previous version of it as well) is finally working.
But wait, but wait. What we've done is to turn this thing into something which:
binds the value of its argument to a variable;
and evaluates some code with that binding.
There's a name for something which does that, and that name is function.
(defun not-sample-macro-any-more (it)
(if (typep it 'sample-class)
(progn
(print "evaluated")
(print (slot-1 it)))))
This does everything that the working versions of sample-macro did but without all the needless complexity.
Well, it doesn't do one thing: it doesn't get expanded inline, and perhaps that means it might be a little slower.
Well, back in the days of coal-fired Lisp this was a real problem. Coal-fired Lisp systems had primitive compilers made of wood shavings and sawdust and ran on computers which were very slow indeed. So people would write things which should semantically be functions as macros so the wood-shaving compiler would inline the code. And sometimes this was even worth it.
But now we have advanced compilers (probably still mostly made of wood shavings and sawdust though) and we can say what we actually mean:
(declaim (inline not-sample-macro-any-more))
(defun not-sample-macro-any-more (it)
(if (typep it 'sample-class)
(progn
(print "evaluated")
(print (slot-1 it)))))
And now you can be reasonably assured that not-sample-macro-any-more will be compiled inline.
Even better in this case (but at the cost of almost certainly not having the inlining stuff):
(defgeneric not-even-slightly-sample-macro (it)
(:method (it)
(declare (ignore it))
nil))
(defmethod not-even-slightly-sample-macro ((it sample-class))
(print "evaluated")
(print (slot-1 it)))
So the summary here is:
Use macros for what they are for, which is transforming source code. If you don't want to do that, use functions. If you are sure the that the act of calling the functions is taking up lots of time, then consider declaring them inline to avoid that.
The other answers explained that macro execution is about transforming source code and values available at macro expansion time.
Let's also try to understand the error message. We need to take it literally:
Execution of a form compiled with errors.
Above says that it is about compilation.
Form:
(SAMPLE-MACRO *SAMPLE-INSTANCE*)
Above is the source form which is to be compiled.
Compile-time error:
(during macroexpansion of (SAMPLE-MACRO *SAMPLE-INSTANCE*))
Again: compilation and now specifically during macro expansion.
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
(*SAMPLE-INSTANCE*).
Now above is the interesting part: there is no applicable method for the generic function SLOT-1 and the argument *SAMPLE-INSTANCE*.
What is *SAMPLE-INSTANCE*? It's a symbol. In your code is a method, but it is for instances of class sample-class. But there is no method for symbols. So this will not work:
(setf p '*sample-instance*)
(slot-1 p)
That's basically what your code did. You expected to work with runtime values, but all you got at compile time was a source symbol...
The compiler error message showing a compile time error with a source-code element is an indication that there is mixup of runtime and macro-expansion time computation.
See also:
The ANSI Standard, Section 7.6.6
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
To understand what the macro is doing, let's use macroexpand.
(macroexpand-1 '(sample-macro *sample-instance*))
=>
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION COMMON-LISP-USER::SLOT-1 (1)>
when called with arguments
(*SAMPLE-INSTANCE*).
[Condition of type SB-PCL::NO-APPLICABLE-METHOD-ERROR]
Oops, same error message. I will simplify the macro and remove the evaluation around slot-1.
(defmacro sample-macro (p)
`(if (typep ,p 'sample-class)
(progn
(print "evaluated")
(print (slot-1 p)))))
(macroexpand-1 '(sample-macro *sample-instance*))
=>
(IF (TYPEP *SAMPLE-INSTANCE* 'SAMPLE-CLASS)
(PROGN (PRINT "evaluated") (PRINT (SLOT-1 P))))
The code looks good until the variable P. So will it work with, simply, ,p? No need to write ,(slot-1 p) since slot-1 is here correctly.
(defmacro sample-macro (p)
`(if (typep ,p 'sample-class)
(progn
(print "evaluated")
(print (slot-1 ,p)))))
(macroexpand-1 '(sample-macro *sample-instance*))
=>
(IF (TYPEP *SAMPLE-INSTANCE* 'SAMPLE-CLASS)
(PROGN (PRINT "evaluated") (PRINT (SLOT-1 *SAMPLE-INSTANCE*))))
The code looks correct.
(sample-macro *sample-instance*)
"evaluated"
"sample slot"
and it works.
Common Lisp built-in functions are probably implemented in C. But I imagine macros are implemented in lisp (sorry if I'm wrong about any of two sentences). Is there any way (through some function or some macro) to see the implementations of built-in macros in Common Lisp? I'm using CLisp.
The ability to inspect function and macro definitions is a feature of your development environment. These days it is typical to use SLIME or SLY with emacs as the basis of a Lisp development environment. I personally use SLIME, but I have heard good things about SLY, too.
In SLIME you can invoke slime-edit-definition (either by keying M-x slime-edit-definition or by using the keybinding M-.) to visit a definition for the symbol under the cursor in a source file. This works both when editing in a source file, or from the REPL. This feature is extremely useful when you want to inspect some library code you are working with, but you can also view a lot of built-in definitions this way. You can even jump to a new definition from a new symbol found in whatever definition you are currently inspecting.
After you are done looking at a definition, you can use M-x slime-pop-find-definition-stack, or the easier to remember keybinding M-, (M-* will also work), to back out through the previously viewed definitions, eventually returning to your starting point.
Here is an example, in SBCL:
CL-USER> with-open-file[press M-.]
(Note that the "[press M-.]" above is not typed, but only meant to remind what action is taken here). With the cursor on or right after the symbol with-open-file, press M-. to see the definition:
(sb-xc:defmacro with-open-file ((stream filespec &rest options)
&body body)
(multiple-value-bind (forms decls) (parse-body body nil)
(let ((abortp (gensym)))
`(let ((,stream (open ,filespec ,#options))
(,abortp t))
,#decls
(unwind-protect
(multiple-value-prog1
(progn ,#forms)
(setq ,abortp nil))
(when ,stream
(close ,stream :abort ,abortp)))))))
This time after keying M-. SLIME gives a choice of definitions to view:
CL-USER> and[press M-.]
Displayed in an emacs buffer:
/path-to-source/sbcl-2.0.4/src/code/macros.lisp
(DEFMACRO AND)
/path-to-source/sbcl-2.0.4/src/pcl/ctypes.lisp
(DEFINE-METHOD-COMBINATION AND)
We want to see the macro definition, so move the cursor to the line showing (DEFMACRO AND), and the following definition is displayed:
;; AND and OR are defined in terms of IF.
(sb-xc:defmacro and (&rest forms)
(named-let expand-forms ((nested nil) (forms forms) (ignore-last nil))
(cond ((endp forms) t)
((endp (rest forms))
(let ((car (car forms)))
(cond (nested
car)
(t
;; Preserve non-toplevelness of the form!
`(the t ,car)))))
((and ignore-last
(endp (cddr forms)))
(car forms))
;; Better code that way, since the result will only have two
;; values, NIL or the last form, and the precedeing tests
;; will only be used for jumps
((and (not nested) (cddr forms))
`(if ,(expand-forms t forms t)
,#(last forms)))
(t
`(if ,(first forms)
,(expand-forms t (rest forms) ignore-last))))))
There is more stuff here, since you are now actually in the source file that contains the definition for and; if you scroll down a bit you can also find the definition for or.
A lot of SBCL functions are written in Lisp; SBCL has a very high-quality compiler, so a lot of stuff that you might otherwise expect to be written in C can be written in Lisp without loss of performance. Here is the definition for the function list-length:
CL-USER> list-length[press M-.]
(defun list-length (list)
"Return the length of the given List, or Nil if the List is circular."
(do ((n 0 (+ n 2))
(y list (cddr y))
(z list (cdr z)))
(())
(declare (type fixnum n)
(type list y z))
(when (endp y) (return n))
(when (endp (cdr y)) (return (+ n 1)))
(when (and (eq y z) (> n 0)) (return nil))))
The same thing can be done when using CLISP with SLIME. Here is with-open-file as defined in CLISP:
CL-USER> with-open-file[press M-.]
(defmacro with-open-file ((stream &rest options) &body body)
(multiple-value-bind (body-rest declarations) (SYSTEM::PARSE-BODY body)
`(LET ((,stream (OPEN ,#options)))
(DECLARE (READ-ONLY ,stream) ,#declarations)
(UNWIND-PROTECT
(MULTIPLE-VALUE-PROG1
(PROGN ,#body-rest)
;; Why do we do a first CLOSE invocation inside the protected form?
;; For reliability: Because the stream may be a buffered file stream,
;; therefore (CLOSE ,stream) may produce a disk-full error while
;; writing the last block of the file. In this case, we need to erase
;; the file again, through a (CLOSE ,stream :ABORT T) invocation.
(WHEN ,stream (CLOSE ,stream)))
(WHEN ,stream (CLOSE ,stream :ABORT T))))))
But, many CLISP functions are written in C, and those definitions are not available to inspect in the same way as before:
CL-USER> list-length[press M-.]
No known definition for: list-length (in COMMON-LISP-USER)
Lisp newbie here.
I am reading Paul Graham's book, ANSI Common Lisp.
On page 38 is an uncompress function. It takes a list of pairs, where the first item in the pair is a number to indicate how many of the second item there should be. For example, uncompressing this:
((3 a) (2 b) c)
should produce this:
(A A A B B C)
I typed the uncompress function into a Lisp interpreter (GCL-2.6.2-ANSI) and then tested it like this:
(uncompress '((3 A) B (2 C) (5 D)))
That produced this error message:
Error in IF [or a callee]: Too many arguments.
Fast links are on: do (use-fast-links nil) for debugging
Broken at IF. Type :H for Help.
1 (Abort) Return to top level.
Below is the uncompress function. I think that I faithfully typed what was written in the book. I have tested each piece and each piece seems to work correctly. Truthfully, I'm stuck. I don't know what's causing the error. I'd appreciate your help.
(defun uncompress (lst)
(if (null lst)
nil
(let (elt (car lst))
(rest (uncompress (cdr lst))))
(if (consp elt)
(append (apply #'list-of elt)
rest)
(cons elt rest))))
(defun list-of (n elt)
(if (zerop n)
nil
(cons elt (list-of (- n 1) elt))))
If you use editor indentation tools, the code looks like this:
(defun uncompress (lst)
(if (null lst)
nil
(let (elt (car lst))
(rest (uncompress (cdr lst))))
(if (consp elt)
(append (apply #'list-of elt)
rest)
(cons elt rest))))
That would have made it easier to spot this error. Syntactically it is wrong, since the IF does not take more than three forms.
(defun uncompress (lst)
(if (null lst) ; the IF has four subforms, one too many
nil
(let (elt (car lst)) ;<- variables ELT and CAR? Makes no sense
(rest (uncompress (cdr lst)))) ; <- not using the result?
(if (consp elt) ; <- fourth form in IF? Does not make sense.
(append (apply #'list-of elt)
rest)
(cons elt rest))))
In Common Lisp both IF and LET are special operators with built-in syntax.
In Lisp the syntax for LET usually is:
let ({var | (var [init-form])}*) form* => result*
In Common Lisp it is possible to add declarations on top of the body forms of the LET:
let ({var | (var [init-form])}*) declaration* form* => result*
The syntax for IF in Common Lisp is:
if test-form then-form [else-form] => result*
Indentation
Generally it is not a good idea to manually indent Lisp code. Let the editor or IDE do it. Make sure that all code is properly indented.
If you have a syntax problem: first re-indent the expression -> this makes sure that the code is properly indented and then makes finding problems easier. Next compile the code and read the compiler error message. Common Lisp has great compilers and some have quite good error reporting.
The code
The code is not great anyway: it uses recursion where higher-order functions exist or a LOOP would be better
This version got both: the higher-order MAPCAN and a LOOP:
(defun uncompress (list)
(mapcan #'expand-item list))
(defun expand-item (item)
(typecase item
(atom (list item))
(cons (destructuring-bind (n element) item
(loop repeat n collect element)))))
You have the most typical common lisp syntax error: incorrect use of parentheses!
Here is the correct version:
(defun uncompress (lst)
(if (null lst)
nil
(let ((elt (car lst))
(rest (uncompress (cdr lst))))
(if (consp elt)
(append (apply #'list-of elt)
rest)
(cons elt rest)))))
Since these kind of errors are so common when no specialized editor is used, I advise you to use an editor like Emacs or Vim to edit your programs.
Triggered from this question about setf expanders: defining setf-expanders in Common Lisp
When writing setf expanders for user-defined getters, I commonly find that there is code duplication in the getter and setter, as far as how the property is retrieved. For example:
CL-USER>
(defun new-car (lst)
(car lst))
NEW-CAR
CL-USER>
(defun (setf new-car) (new-value lst)
(setf (car lst) new-value))
(SETF NEW-CAR)
CL-USER>
(defparameter *lst* (list 5 4 3))
*LST*
CL-USER>
*lst*
(5 4 3)
CL-USER>
(setf (new-car *lst*) 3)
3
CL-USER>
*lst*
(3 4 3)
CL-USER>
Note how the (car lst) form, the actual accessor that already has a setf expander defined, is in both defuns. This has always annoyed me somewhat. It would be nice to be able to say on the first defun, 'hey, I'm defining a defun that's a getter, but I also want it to have a typical setf expander'.
Is there any way with the common lisp standard to express this? Has anyone else worried about this issue, and defined a macro that does this?
To be clear, what I'd like here is a way to define a getter and typical setter, where the way that the getter compiles down to common lisp forms that already have setters ((car lst), e.g.) is written only once in the code.
I also understand there are times where you wouldn't want to do this, b/c the setter needs to perform some side effects before setting the value. Or it's an abstraction that actually sets multiple values, or whatever. This question is less relevant in that situation. What I'm talking about here is the case where the setter does the standard thing, and just sets the place of the getter.
What you want can be achieved with the use of macros.
(defmacro define-place (name lambda-list sexp)
(let ((value-var (gensym)))
`(progn
(defun ,name ,lambda-list
,sexp)
(defun (setf ,name) (,value-var ,#lambda-list)
(setf ,sexp ,value-var)))))
(define-place new-chr (list)
(car list))
More information on macros can be found in Peter Seibel's book, Practical Common Lisp. Chapter 10 of Paul Graham's book "ANSI Common Lisp" is another reference.
Note how the (car lst) form, the actual accessor that already has a setf expander defined, is in both defuns.
But that's only apparently true before macro expansion. In your setter, the (car lst) form is the target of an assignment. It will expand to something else, like the call to some internal function that resembles rplaca:
You can do a similar thing manually:
(defun new-car (lst)
(car lst))
(defun (setf new-car) (new-value lst)
(rplaca lst new-value)
new-value)
Voilà; you no longer have duplicate calls to car; the getter calls car, and the setter rplaca.
Note that we manually have to return new-value, because rplaca returns lst.
You will find that in many Lisps, the built-in setf expander for car uses an alternative function (perhaps named sys:rplaca, or variations thereupon) which returns the assigned value.
The way we generally minimize code duplication when defining new kinds of places in Common Lisp is to use define-setf-expander.
With this macro, we associate a new place symbol with two items:
a macro lambda list which defines the syntax for the place.
a body of code which calculates and returns five pieces of information, as five return values. These are collectively called the "setf expansion".
The place-mutating macros like setf use the macro lambda list to destructure the place syntax and invoke the body of code which calculates those five pieces. Those five pieces are then used to generate the place accessing/updating code.
Note, nevertheless, that the last two items of the setf expansion are the store form and the access form. We can't get away from this duality. If we were defining the setf expansion for a car-like place, our access form would invoke car and the store form would be based on rplaca, ensuring that the new value is returned, just like in the above two functions.
However there can exist places for which a significant internal calculation can be shared between the access and the store.
Suppose we were defining my-cadar instead of my-car:
(defun new-cadar (lst)
(cadar lst))
(defun (setf new-cadar) (new-value lst)
(rplaca (cdar lst) new-value)
new-value)
Note how if we do (incf (my-cadar place)), there is a wasteful duplicate traversal of the list structure because cadar is called to get the old value and then cdar is called again to calculate the cell where to store the new value.
By using the more difficult and lower level define-setf-expander interface, we can have it so that the cdar calculation is shared between the access form and the store form. So that is to say (incf (my-cadar x)) will calculate (cadr x) once and store that to a temporary variable #:c. Then the update will take place by accessing (car #:c), adding 1 to it, and storing it with (rplaca #:c ...).
This looks like:
(define-setf-expander my-cadar (cell)
(let ((cell-temp (gensym))
(new-val-temp (gensym)))
(values (list cell-temp) ;; these syms
(list `(cdar ,cell)) ;; get bound to these forms
(list new-val-temp) ;; these vars receive the values of access form
;; this form stores the new value(s) into the place:
`(progn (rplaca ,cell-temp ,new-val-temp) ,new-val-temp)
;; this form retrieves the current value(s):
`(car ,cell-temp))))
Test:
[1]> (macroexpand '(incf (my-cadar x)))
(LET* ((#:G3318 (CDAR X)) (#:G3319 (+ (CAR #:G3318) 1)))
(PROGN (RPLACA #:G3318 #:G3319) #:G3319)) ;
T
#:G3318 comes from cell-temp, and #:G3319 is the new-val-temp gensym.
However, note that the above defines only the setf expansion. With the above, we can only use my-cadar as a place. If we try to call it as a function, it is missing.
Working from Mark's approach, Rainer's post on macro-function, and Amalloy's post on transparent macrolet, I came up with this:
(defmacro with-setters (&body body)
`(macrolet ((defun-mod (name args &body body)
`(,#(funcall (macro-function 'defun)
`(defun ,name ,args ,#body) nil))))
(macrolet ((defun (name args &body body)
`(progn
(defun-mod ,name ,args ,#body)
(defun-mod (setf ,name) (new-val ,#args)
(setf ,#body new-val)))))
(progn
,#body))))
To use:
Clozure Common Lisp Version 1.8-r15286M (DarwinX8664) Port: 4005 Pid: 41757
; SWANK 2012-03-06
CL-USER>
(with-setters
(defun new-car (lst)
(car lst))
(defun new-first (lst)
(first lst)))
(SETF NEW-FIRST)
CL-USER>
(defparameter *t* (list 5 4 3))
*T*
CL-USER>
(new-car *t*)
5
CL-USER>
(new-first *t*)
5
CL-USER>
(setf (new-first *t*) 3)
3
CL-USER>
(new-first *t*)
3
CL-USER>
*t*
(3 4 3)
CL-USER>
(setf (new-car *t*) 9)
9
CL-USER>
*t*
(9 4 3)
There are some variable capture issues here that should probably be attended to, before using this macro in production code.
The only thing I don't like about Emacs is the lack of namespaces, so I'm wondering if I can implement them on my own.
This is my first attempt, and it's obvious that I can't just replace every match of a name with its prefixed version, but what should I check? I can check for bindings with (let) then mark the entire subtree, but what if somebody creates a (my-let) function that uses let? Is my effort destined to fail? :(
Also, why are my defuns failing to define the function? Do I have to run something similar to intern-symbol on every new token?
Thanks!
Since this is the first google result for elisp namespaces...
There's a minimalist implementation of namespaces called fakespace which you can get on elpa, which does basic encapsulation. I'm working on something ambitious myself, which you can check out here.
To handle things like my-let or my-defun, you need to macroexpand those definitions, e.g. with macroexpand-all.
For the failure to define the functions, you need to use intern instead of make-symbol (because make-symbol always creates a new distinct fresh uninterned symbol).
Adding namespaces will take more than prefixing the identifiers with the namespace names. The interpreter has to be able to tell the namespaces. Some tinkering must go into the interpreter as well. That might need to go through a thorough discussion at gnu.emacs.sources and/or #emacs at irc.freenode.org.
This is a fixed version of the code from #vpit3833 to provide namespace support (using the hint from #Stefan). It’s too good to leave around half-fixed :)
;; Simple namespace definitions for easier elisp writing and clean
;; access from outside. Pythonesque elisp :)
;;
;; thanks to vpit3833 → http://6e5e5ae9206fa093.paste.se/
(defmacro namespace (prefix &rest sexps)
(let* ((naive-dfs-map
(lambda (fun tree)
(mapcar (lambda (n) (if (listp n) (funcall naive-dfs-map fun n)
(funcall fun n))) tree)))
(to-rewrite (loop for sexp in sexps
when (member (car sexp)
'(defvar defmacro defun))
collect (cadr sexp)))
(fixed-sexps (funcall naive-dfs-map
(lambda (n) (if (member n to-rewrite)
(intern
(format "%s-%s" prefix n)) n))
sexps)))
`(progn ,#fixed-sexps)))
;; (namespace test
;; (defun three () 3)
;; (defun four () (let ((three 4)) three))
;; (defun + (&rest args) (apply #'- args)))
;; (test-+ 1 2 3)
(provide 'namespace)