Macros That Write Macros - Compile Error - macros

When I compile the following code, SBCL complains that g!-unit-value and g!-unit are undefined. I'm not sure how to debug this. As far as I can tell, flatten is failing.
When flatten reaches the unquoted part of defunits, it seems like the entire part is being treated as an atom. Does that sound correct?
The following uses code from the book Let over Lambda:
Paul Graham Utilities
(defun symb (&rest args)
(values (intern (apply #'mkstr args))))
(defun mkstr (&rest args)
(with-output-to-string (s)
(dolist (a args) (princ a s))))
(defun group (source n)
(if (zerop n) (error "zero length"))
(labels ((rec (source acc)
(let ((rest (nthcdr n source)))
(if (consp rest)
(rec rest (cons (subseq source 0 n) acc))
(nreverse (cons source acc))))))
(if source (rec source nil) nil)))
(defun flatten (x)
(labels ((rec (x acc)
(cond ((null x) acc)
((atom x) (cons x acc))
(t (rec (car x) (rec (cdr x) acc))))))
(rec x nil)))
Let Over Lambda Utilities - Chapter 3
(defmacro defmacro/g! (name args &rest body)
(let ((g!-symbols (remove-duplicates
(remove-if-not #'g!-symbol-p
(flatten body)))))
`(defmacro ,name ,args
(let ,(mapcar
(lambda (g!-symbol)
`(,g!-symbol (gensym ,(subseq
(symbol-name g!-symbol)
2))))
g!-symbols)
,#body))))
(defun g!-symbol-p (symbol-to-test)
(and (symbolp symbol-to-test)
(> (length (symbol-name symbol-to-test)) 2)
(string= (symbol-name symbol-to-test)
"G!"
:start1 0
:end1 2)))
(defmacro defmacro! (name args &rest body)
(let* ((o!-symbols (remove-if-not #'o!-symbol-p args))
(g!-symbols (mapcar #'o!-symbol-to-g!-symbol o!-symbols)))
`(defmacro/g! ,name ,args
`(let ,(mapcar #'list (list ,#g!-symbols) (list ,#o!-symbols))
,(progn ,#body)))))
(defun o!-symbol-p (symbol-to-test)
(and (symbolp symbol-to-test)
(> (length (symbol-name symbol-to-test)) 2)
(string= (symbol-name symbol-to-test)
"O!"
:start1 0
:end1 2)))
(defun o!-symbol-to-g!-symbol (o!-symbol)
(symb "G!" (subseq (symbol-name o!-symbol) 2)))
Let Over Lambda - Chapter 5
(defun defunits-chaining (u units prev)
(if (member u prev)
(error "~{ ~a~^ depends on~}"
(cons u prev)))
(let ((spec (find u units :key #'car)))
(if (null spec)
(error "Unknown unit ~a" u)
(let ((chain (second spec)))
(if (listp chain)
(* (car chain)
(defunits-chaining
(second chain)
units
(cons u prev)))
chain)))))
(defmacro! defunits (quantity base-unit &rest units)
`(defmacro ,(symb 'unit-of- quantity)
(,g!-unit-value ,g!-unit)
`(* ,,g!-unit-value
,(case ,g!-unit
((,base-unit) 1)
,#(mapcar (lambda (x)
`((,(car x))
,(defunits-chaining
(car x)
(cons
`(,base-unit 1)
(group units 2))
nil)))
(group units 2))))))

This is kind of tricky:
Problem: you assume that backquote/comma expressions are plain lists.
You need to ask yourself this question:
What is the representation of a backquote/comma expression?
Is it a list?
Actually the full representation is unspecified. See here: CLHS: Section 2.4.6.1 Notes about Backquote
We are using SBCL. See this:
* (setf *print-pretty* nil)
NIL
* '`(a ,b)
(SB-INT:QUASIQUOTE (A #S(SB-IMPL::COMMA :EXPR B :KIND 0)))
So a comma expression is represented by a structure of type SB-IMPL::COMMA. The SBCL developers thought that this representation helps when such backquote lists need to be printed by the pretty printer.
Since your flatten treats structures as atoms, it won't look inside...
But this is the specific representation of SBCL. Clozure CL does something else and LispWorks again does something else.
Clozure CL:
? '`(a ,b)
(LIST* 'A (LIST B))
LispWorks:
CL-USER 87 > '`(a ,b)
(SYSTEM::BQ-LIST (QUOTE A) B)
Debugging
Since you found out that somehow flatten was involved, the next debugging steps are:
First: trace the function flatten and see with which data it is called and what it returns.
Since we are not sure what the data actually is, one can INSPECT it.
A debugging example using SBCL:
* (defun flatten (x)
(inspect x)
(labels ((rec (x acc)
(cond ((null x) acc)
((atom x) (cons x acc))
(t (rec (car x) (rec (cdr x) acc))))))
(rec x nil)))
STYLE-WARNING: redefining COMMON-LISP-USER::FLATTEN in DEFUN
FLATTEN
Above calls INSPECT on the argument data. In Common Lisp, the Inspector usually is something where one can interactively inspect data structures.
As an example we are calling flatten with a backquote expression:
* (flatten '`(a ,b))
The object is a proper list of length 2.
0. 0: SB-INT:QUASIQUOTE
1. 1: (A ,B)
We are in the interactive Inspector. The commands now available:
> help
help for INSPECT:
Q, E - Quit the inspector.
<integer> - Inspect the numbered slot.
R - Redisplay current inspected object.
U - Move upward/backward to previous inspected object.
?, H, Help - Show this help.
<other> - Evaluate the input as an expression.
Within the inspector, the special variable SB-EXT:*INSPECTED* is bound
to the current inspected object, so that it can be referred to in
evaluated expressions.
So the command 1 walks into the data structure, here a list.
> 1
The object is a proper list of length 2.
0. 0: A
1. 1: ,B
Walk in further:
> 1
The object is a STRUCTURE-OBJECT of type SB-IMPL::COMMA.
0. EXPR: B
1. KIND: 0
Here the Inspector tells us that the object is a structure of a certain type. That's what we wanted to know.
We now leave the Inspector using the command q and the flatten function continues and returns a value:
> q
(SB-INT:QUASIQUOTE A ,B)

For anyone else who is trying to get defmacro! to work on SBCL, a temporary solution to this problem is to grope inside the unquote structure during the flatten procedure recursively flatten its contents:
(defun flatten (x)
(labels ((flatten-recursively (x flattening-list)
(cond ((null x) flattening-list)
((eq (type-of x) 'SB-IMPL::COMMA) (flatten-recursively (sb-impl::comma-expr x) flattening-list))
((atom x) (cons x flattening-list))
(t (flatten-recursively (car x) (flatten-recursively (cdr x) flattening-list))))))
(flatten-recursively x nil)))
But this is horribly platform dependant. If I find a better way, I'll post it.

In case anyone's still interested in this one, here are my three cents. My objection to the above modification of flatten is that it might be more naturally useful as it were originally, while the problem with representations of unquote is rather endemic to defmacro/g!. I came up with a not-too-pretty modification of defmacro/g! using features to decide what to do. Namely, when dealing with non-SBCL implementations (#-sbcl) we proceed as before, while in the case of SBCL (#+sbcl) we dig into the sb-impl::comma structure, use its expr attribute when necessary and use equalp in remove-duplicates, as we are now dealing with structures, not symbols. Here's the code:
(defmacro defmacro/g! (name args &rest body)
(let ((syms (remove-duplicates
(remove-if-not #-sbcl #'g!-symbol-p
#+sbcl #'(lambda (s)
(and (sb-impl::comma-p s)
(g!-symbol-p (sb-impl::comma-expr s))))
(flatten body))
:test #-sbcl #'eql #+sbcl #'equalp)))
`(defmacro ,name ,args
(let ,(mapcar
(lambda (s)
`(#-sbcl ,s #+sbcl ,(sb-impl::comma-expr s)
(gensym ,(subseq
#-sbcl
(symbol-name s)
#+sbcl
(symbol-name (sb-impl::comma-expr s))
2))))
syms)
,#body))))
It works with SBCL. I have yet to test it thoroughly on other implementations.

Related

Check for proper list in Common Lisp

Is there a standard function in Common Lisp that can check against improper lists (i.e. circular and dotted lists) without signaling an error? list-length can check against circular lists (it returns nil for them), but signals type-error when given a dotted list.
Scheme's list? traverses the whole list to make sure it is not dotted or circular; Common Lisp's listp only checks that it's given nil or a cons cell.
Here's the simplest I could come up with:
(defun proper-list-p (x)
(not (null (handler-case (list-length x) (type-error () nil)))))
Since several implementations have been suggested and many unexpected problems have been found, here's a test suite for aspiring proper-list-p writers:
(defun circular (xs)
(let ((xs (copy-list xs)))
(setf (cdr (last xs)) xs)
xs))
(assert (eql t (proper-list-p '())))
(assert (eql t (proper-list-p '(1))))
(assert (eql t (proper-list-p '(1 2))))
(assert (eql t (proper-list-p '(1 2 3))))
(assert (not (proper-list-p 1)))
(assert (not (proper-list-p '(1 . 2))))
(assert (not (proper-list-p '(1 2 . 3))))
(assert (not (proper-list-p '(1 2 3 . 4))))
(assert (not (proper-list-p (circular '(1)))))
(assert (not (proper-list-p (circular '(1 2)))))
(assert (not (proper-list-p (circular '(1 2 3)))))
(assert (not (proper-list-p (list* 1 (circular '(2))))))
(assert (not (proper-list-p (list* 1 2 (circular '(3 4))))))
There is no standard function to do this, perhaps because such a function was seen as rather expensive if it was to be correct, but, really, this just seems like am omission from the language to me.
A minimal (not very performant) implementation, which does not rely on handling errors (Python people think that's a reasonable way to program, I don't, although this is a stylistic choice), is, I think
(defun proper-list-p (l)
(typecase l
(null t)
(cons
(loop for tail = l then (cdr tail)
for seen = (list tail) then (push tail seen)
do (cond ((null tail)
(return t))
((not (consp tail))
(return nil))
((member tail (rest seen))
(return nil)))))))
This takes time quadratic in the length of l, and conses proportional to the length of l. You can obviously do better using an hashtable for the occurs check, and you can use a tortoise-&-hare algorithm do avoid the occurs check (but I'm not sure what the complexity of that is off the top of my head).
I am sure there are much better functions than this in libraries. In particular Alexandria has one.
While thinking about this question, I also wrote this function:
(defun classify-list (l)
"Classify a possible list, returning four values.
The first value is a symbol which is
- NULL if the list is empty;
- LIST if the list is a proper list;
- CYCLIC-LIST if it contains a cycle;
- IMPROPER-LIST if it does not end with nil;
- NIL if it is not a list.
The second value is the total number of conses in the list (following
CDRs only). It will be 0 for an empty list or non-list.
The third value is the cons at which the cycle in the list begins, or
NIL if there is no cycle or the list isn't a list.
The fourth value is the number if conses in the cycle, or 0 if there is no cycle.
Note that you can deduce the length of the leading element of the list
by subtracting the total number of conses from the number of conses in
the cycle: you can then use NTHCDR to pull out the cycle."
;; This is written as a tail recursion, I know people don't like
;; that in CL, but I wrote it for me.
(typecase l
(null (values 'null 0 nil 0 0))
(cons
(let ((table (make-hash-table)))
(labels ((walk (tail previous-tail n)
(typecase tail
(null
(values 'list n nil 0))
(cons
(let ((m (gethash tail table nil)))
(if m
(values 'cyclic-list n tail (- n m))
(progn
(setf (gethash tail table) n)
(walk (cdr tail) tail (1+ n))))))
(t
(values 'improper-list n previous-tail 0)))))
(walk l nil 0))))
(t (values nil 0 nil 0))))
This can be used to get a bunch of information about a list: how long it is, if it is proper, if not if it's cyclic, and where the cycle is. Beware that in the cases of cyclic lists this will return circular structure as its third value. I believe that you need to use an occurs check to do this – tortoise & hare will tell you if a list is cyclic, but not where the cycle starts.
in addition, something slightly less verbose, than the accepted answer:
(defun improper-tail (ls)
(do ((x ls (cdr x))
(visited nil (cons x visited)))
((or (not (consp x)) (member x visited)) x)))
(defun proper-list-p (ls)
(null (improper-tail ls)))
or just like this:
(defun proper-list-p (ls)
(do ((x ls (cdr x))
(visited nil (cons x visited)))
((or (not (consp x)) (member x visited)) (null x))))
seen to pass all the op's test assertions
After our hopeless attempts with tailp, here, sth which uses the
sharp-representation of circular lists :) .
With regex (to detect circular sublist)
(setf *print-circle* t)
(ql:quickload :cl-ppcre)
(defun proper-listp (lst)
(or (null lst) ; either a `'()` or:
(and (consp lst) ; a cons
(not (cl-ppcre::scan "#\d+=(" (princ-to-string lst)))) ; not circular
(null (cdr (last lst)))))) ; not a dotted list
Without regex (cannot detect circular sublists)
(defun proper-listp (lst)
(or (null lst) ; either a `'()` or:
(and (consp lst) ; a cons
(not (string= "#" (subseq (princ-to-string lst) 0 1))) ; not circular
(null (cdr (last lst)))))) ; not a dotted list
(tailp l (cdr l)) is t for circular lists but nil for non-circular lists.
Credits to #tfp and #RainerJoswig who taught me this here .
So, your function would be:
(defun proper-listp (lst)
(or (null lst) ; either a `'()` or:
(and (consp lst) ; a cons
(not (tailp lst (cdr lst))) ; not circular
(null (cdr (last lst)))))) ; not a dotted list
By the way, I use proper-listp by purpose. Correct would be - by convetion proper-list-p. However, this name is already occupied in the CLISP implementation by SYSTEM::%PROPER-LIST-Pwhy the definition of the function raises a continuable error.
Conclusion of our discussion in the comment section:
The behavior of tailp for circular lists is undefined. Therefore this answer is wrong! Thank you #Lassi for figuring this out!

How to write LISP macro with double quasi quotation in scheme

I need to write the lisp macro in scheme (please on hygienic macros and syntax-rules etc) that will have function call and Alist as argument
I want function and macro that call that function to have syntax like this:
(foo '(10 (a (lambda () (display "10")) b (lambda () (display "20"))))
or macro without quotes.
My last code is working, but not sure if this is how you suppose to write function/macro like this. It seems that I need double backquote but don't know how to write it. (I'm right now reading On Lips by Paul Graham and he said that double backquote is very hard and only need by macros defining macros, but It seems that this is what I need).
(define (foo expr)
`(list ,(car expr)
(,(string->symbol "quasiquote") ,(pair-map (lambda (a b)
(cons (symbol->string a)
(list 'unquote b)))
(cadr expr)))))
(define-macro (bar expr)
(foo expr))
(define xx (bar (10 (a 20 b (lambda () (display "x") (newline))))))
;; (list 10 `((a . ,20) (b . ,(lambda () (display "x") (newline))))
(define bfn (cdr (assoc "b" (cadr xx)))))
(bfn)
;; "x"
and here is definition of pair-map
(define (pair-map fn seq-list)
"(seq-map fn list)
Function call fn argument for pairs in a list and return combined list with
values returned from function fn. It work like the map but take two items from list"
(let iter ((seq-list seq-list) (result '()))
(if (null? seq-list)
result
(if (and (pair? seq-list) (pair? (cdr seq-list)))
(let* ((first (car seq-list))
(second (cadr seq-list))
(value (fn first second)))
(if (null? value)
(iter (cddr seq-list) result)
(iter (cddr seq-list) (cons value result))))))))
with (string->symbol "quasiquote") I was able not to use double backquote, can this be written with double backquote/quasiquote? How this should look like?
I'm asking if this can be written different way so I can fix few issues in my own lisp interpreter (not sure if is working correctly but it seems that this final version works the same in guile).
I came up with shorter quasiquote version, but still it require inserting symbols:
(define (foo expr)
`(list ,(car expr)
(,'quasiquote ,(pair-map (lambda (a b)
`(,(symbol->string a) . (,'unquote ,b)))
(cadr expr)))))

Scheme; Error Holes in a Macro List

So for a college assignment we've been asked to work with macros and I'm finding it hard to understand how to implement code in scheme (we went from reversing a string to building an interpreter in one lecture).
(define macro-alist
`((and ,(λ (e)
(let ((forms (cdr e)))
(cond ((null? forms) '#t)
((null? (cdr forms)) (car forms))
(else `(if ,(car forms) (and ,#(cdr forms)) #f))))))
;(or ,error)
;(let ,error)
;(cond ,error)
(if ,(λ (e) (let ((guard (cadr e))
(then-part (caddr e))
(else-part (cadddr e)))
`((%if ,guard (λ () ,then-part) (λ () ,else-part))))))
))
We were asked to 'fill in the error holds in macro-alist' for the weekend and I'm finding it difficult.
I found some resources and combining them with my own brief knowledge I have :
`((or ,(lambda (e)
(and (list-strictly-longer-than? e 0)
(equal? (list-ref e 0) 'or)
(letrec ([visit (lambda (i)
(if(null? i)
#t
(and (is-exression? (car i))
(visit (cdr i)))))])
(visit (cdr e)))))))
`((let ,(lambda (e)
(and (proper-list-of-given-length? e 3)
(equal? (car e) 'let)
(list? (cadr e))
(is-expression? (list-ref e 2))
(lectrec ([visit (trace-lambda visit (i a)
(if(null? i)
#t
(and (proper-list-of-given-length? (car i) 2)
(is-identifier? (caar i))
(is-expression? (cadar i))
(not (member (caar i) a))
(visit (cdr i) (cons (caar i) a)))))])
(visit (cadr e) '()))))))
`((cond ,(lambda (e)
(and (list-strictly-longer-than? e 1)
(equal? (car v) 'cond)
(lectrec ([visit (lambda (i)
(if (null? (cdr i))
(is-else-clause? (car i))
(if (pair? (cdr i))
(and (cond? (car i))
(visit (cdr i))))))])
(visit (cdr e)))))))
For or, let and cond. I'm wondering if these are correct or if I'm close. I don't understand much about macros or scheme in general so some information/help on what to do would be appreciated.
If you look at the implementation of and:
(define expand-and
(λ (e)
(let ((forms (cdr e)))
(cond ((null? forms) '#t)
((null? (cdr forms)) (car forms))
(else `(if ,(car forms) (and ,#(cdr forms)) #f))))))
(expand-and '(and)) ; ==> #t
(expand-and '(and a)) ; ==> a
(expand-and '(and a b)) ; ==> (if a (and b) #f)
I notice two things. It doesn't really double check that the first element is and or if it's a list. Perhaps the interpreter doesn't use this unless it has checked this already?
Secondly it doesn't seem like you need to expand everything. As you see you might end up with some code + and with fewer arguments. No need for recursion since the evaluator will do that for you.
I think you are overthinking it. For or it should be very similar:
(expand-or '(or)) ; ==> #f
(expand-and '(or a b c)) ; ==> (let ((unique-var a)) (if unique-var unique-var (or b c)))
The let binding prevents double evaluation of a but if you have no side effects you might just rewrite it to (if a a (or b)). As with and or might expand to use or with fewer arguments than the original. This trick you can do with cond as well:
(cond (a b c)
...) ; ==>
(if a
(begin b c)
(cond ...))
let does not need this since it's perhaps the simplest one if you grasp map:
(let ((a x) (c y))
body ...) ; ==>
((lambda (a c) body ...) x y)
The report has examples of how the macros for these are made, but they might not be the simplest to rewrite to functions that takes code as structure like your interpeter. However using the report to understand the forms would perhaps worked just as well as posting a question here on SO.

Not numeric atoms LISP

I want to ask why this function doesn't work...
(defun nenum(ls)
(cond
((null ls) nil)
((listp car(ls)) (nenum (rest ls)))
((numberp car(ls)) (nenum (rest ls)))
(t (cons (car ls) (nenum (rest ls))))))
Example: (nenum '(l 1 i (b) (5) s -2 p)) --> (l i s p)
Thank you!
Looking at the predicate you have in one of your cond terms:
(listp car (ls))
Thus apply the function listp with the two arguments car and the result of calling the function ls with no arguments. car and ls both need to be free variables and listp needs to be a different function than the one defined in CLHS since it only takes one argument.
Perhaps you have though you were writing Algol? An Algol function call look like operator(operand) but not CL. CL is a LISP dialect and we have this form on our function calls:
(operand operator)
If we nest we do the same:
(operand (operand operator))
You got it right in the alternative (cons (car ls) (nenum (rest ls)))
Replace car(ls) with (car ls).
Here's a much easier way to write that function:
(defun nenum (list)
(remove-if (lambda (item)
(or (listp item)
(numberp item)))
list))
Note that NIL doesn't need its own test because listp covers it.
There's no need to write a function like this from scratch. Common Lisp already provides remove-if, and you can give it a predicate that matches numbers and non-atoms:
CL-USER> (remove-if #'(lambda (x)
(or (numberp x)
(not (atom x))))
'(l 1 i (b) (5) s -2 p))
;=> (L I S P)
Or, to make it even clearer that you're keeping non-numeric atoms, you can use remove-if-not with a predicate that checks for numeric atoms:
CL-USER> (remove-if-not #'(lambda (x)
(and (atom x)
(not (numberp x))))
'(l 1 i (b) (5) s -2 p))
;=> (L I S P)
Note that the empty list, which is often written as (), is just the symbol nil. As such, it too is a non-numeric atom. If you'd want to keep other symbols, e.g.,
CL-USER> (remove-if-not #'(lambda (x)
(and (atom x)
(not (numberp x))))
'(li (b) -1 (5) sp))
;=> (LI SP)
then you'll probably want to keep nil as well:
CL-USER> (remove-if-not #'(lambda (x)
(and (atom x)
(not (numberp x))))
'(van (b) () (5) a))
;=> (VAN NIL A)

Learning Lisp. Can't seem to get a value from one function and use it within another

I'm trying to find the maximum number within a list, then do something with it:
(defun maxList (l)
(if (= (length l) 1)
(first l)
(if (> (first l) (maxList (rest l)))
(first l)
(maxList (rest l))
);if
);if
);defun
(defun try-let (l)
(let (a (maxList l))
(print a)
);let
);defun
However it prints null, yet maxList works. What am I doing wrong ?
You're missing a pair of parentheses:
(let ((a (maxList l)))
This is because let takes a list of bindings as in
(let ((a 1) (b 2) (c 'foo))
expr)
so in this case you have to pass a one-element list containing the binding (a (maxList l))
(defun maxList (l)
(if (= (length l) 1)
Calling LENGTH is not a good idea. It traverses the whole list.
(first l)
(if (> (first l) (maxList (rest l)))
(first l)
(maxList (rest l)))))
Above calls MAXLIST twice. Maybe here a LET is useful? How about the function MAX?
If you compile your function, a Common Lisp system will complain.
CL-USER 35 > (defun try-let (l)
(let (a (maxList l))
(print a)))
TRY-LET
CL-USER 36 > (compile 'try-let)
;;;*** Warning in TRY-LET: MAXLIST is bound but not referenced
This shows that the Lisp compiler thinks MAXLIST is a variable. Something is wrong. Next look up the syntax of LET.
See Special Operator LET, LET*
let ({var | (var [init-form])}*) declaration* form* => result*
Which says that it is a list of variables or a list of (variable initform). So you can see that you have missed to make it a list. You have just written one binding.