From: Delaregue
Subject: program stack overflow
Date: 
Message-ID: <6b4aa54a.0108270822.6e48d45@posting.google.com>
Hello,

When I am on the top level of Clisp the following code will work:

(defparameter *days* '(
"Sunday" "Monday" "Tuesday" "Wednesday" 
"Thursday" "Friday" "Saturday"))

(defmacro print-days (d &key l)
  `(let ((print-attr (first ,d)))
     (if (null ,d)
	 nil
       (progn
	 (if (equal ,l "short") 
	     (setf print-attr (subseq (first ,d) 0 3)))
	 (format t "~D " print-attr)
	 (print-days (rest ,d) :l ,l)))))

(print-days *days* :l "short")

When I try to compile the code, I am getting a stack overflow.
I usually get the opposit error when doing recursion in lisp (I think
it is because compiling optimize tail-recursion). But this is my first
recursive *macro* so I must forget something.

Do you have an idea?

Delaregue.

From: Kent M Pitman
Subject: Re: program stack overflow
Date: 
Message-ID: <sfw1ylx307e.fsf@world.std.com>
·········@netscape.net (Delaregue) writes:

> 
> Hello,
> 
> When I am on the top level of Clisp the following code will work:
> 
> (defparameter *days* '(
> "Sunday" "Monday" "Tuesday" "Wednesday" 
> "Thursday" "Friday" "Saturday"))
> 
> (defmacro print-days (d &key l)
>   `(let ((print-attr (first ,d)))
>      (if (null ,d)
> 	 nil
>        (progn
> 	 (if (equal ,l "short") 
> 	     (setf print-attr (subseq (first ,d) 0 3)))
> 	 (format t "~D " print-attr)
> 	 (print-days (rest ,d) :l ,l)))))
> 
> (print-days *days* :l "short")
> 
> When I try to compile the code, I am getting a stack overflow.
> I usually get the opposit error when doing recursion in lisp (I think
> it is because compiling optimize tail-recursion). But this is my first
> recursive *macro* so I must forget something.
> 
> Do you have an idea?
> 
> Delaregue.


Your macro unconditionally expands into code that contains a reference 
to itself.  The fact that the expansion is unconditional means that the
embedded reference will also unconditionally expand, and so on.  That's
definitely an infinite loop.

You need a test for d being null.

Incidentally, this thing you are trying to do should NOT be done
with macros.  Use regular functions.  Remove the backquote and the commas
and change defmacro to defun.  You'll still need an end test though, or
it will infinitely recurse at runtime instead of at macroexpansion time.
From: Delaregue
Subject: Re: program stack overflow
Date: 
Message-ID: <6b4aa54a.0108280409.4e4a2b9@posting.google.com>
> Your macro unconditionally expands into code that contains a reference 
> to itself.  The fact that the expansion is unconditional means that the
> embedded reference will also unconditionally expand, and so on.  That's
> definitely an infinite loop.
> 
> You need a test for d being null.
> 
> Incidentally, this thing you are trying to do should NOT be done
> with macros.  Use regular functions.  Remove the backquote and the commas
> and change defmacro to defun.  You'll still need an end test though, or
> it will infinitely recurse at runtime instead of at macroexpansion time.

I am not sure to understand why it does not work.

I take your point that I do not need a macro here but I really would
like to see a working version of this macro.

Can you show me one if you have some time?
From: Kent M Pitman
Subject: Re: program stack overflow
Date: 
Message-ID: <sfw4rqswdmd.fsf@world.std.com>
·········@netscape.net (Delaregue) writes:

> 
> > Your macro unconditionally expands into code that contains a reference 
> > to itself.  The fact that the expansion is unconditional means that the
> > embedded reference will also unconditionally expand, and so on.  That's
> > definitely an infinite loop.
> > 
> > You need a test for d being null.
> > 
> > Incidentally, this thing you are trying to do should NOT be done
> > with macros.  Use regular functions.  Remove the backquote and the commas
> > and change defmacro to defun.  You'll still need an end test though, or
> > it will infinitely recurse at runtime instead of at macroexpansion time.
> 
> I am not sure to understand why it does not work.
> 
> I take your point that I do not need a macro here but I really would
> like to see a working version of this macro.
> 
> Can you show me one if you have some time?

Your original code:

(defparameter *days* '(
"Sunday" "Monday" "Tuesday" "Wednesday" 
"Thursday" "Friday" "Saturday"))

(defmacro print-days (d &key l)
  `(let ((print-attr (first ,d)))
     (if (null ,d)
	 nil
       (progn
	 (if (equal ,l "short") 
	     (setf print-attr (subseq (first ,d) 0 3)))
	 (format t "~D " print-attr)
	 (print-days (rest ,d) :l ,l)))))

(print-days *days* :l "short")

You really should have written:

(defun print-days (d &key l)
  (when d  ;<-- termination condition
    (let ((print-attr (first d)))
      (if (null d)
	  nil
	(progn
	  (if (equal l "short") 
	      (setf print-attr (subseq (first d) 0 3)))
	  (format t "~D " print-attr)
	  (print-days (rest d) :l l))))))

The reason that it doesn't work as a macro is that the argument d is
not evaluated.  It is just a symbol.  You could call EVAL of course, 
but the value is "not ready".  The symbol from the DEFPARAMETER will 
have a value at runtime, but does not have one at compile time.
Given your definition (as a macro), 

  (print-days *days* :l "short")

macroexpands into 

  (let ((print-attr (first *days*)))
     (if (null *days*)
	 nil
       (progn
	 (if (equal "short" "short") 
	     (setf print-attr (subseq (first *days*) 0 3)))
	 (format t "~D " print-attr)
	 (print-days (rest *days*) :l "short"))))

However, this contains a call to PRINT-DAYS.  If you expand

  (print-days (rest *days*) :l "short")

you'll get:

  (let ((print-attr (first (rest *days*))))
     (if (null (rest *days*))
	 nil
       (progn
	 (if (equal "short" "short") 
	     (setf print-attr (subseq (first (rest *days*)) 0 3)))
	 (format t "~D " print-attr)
	 (print-days (rest (rest *days*)) :l "short"))))

However, this has a call to PRINT-DAYS.  If you expand it,
you'll get: 

  (let ((print-attr (first (rest *days*))))
     (if (null (rest *days*))
	 nil
       (progn
	 (if (equal "short" "short") 
	     (setf print-attr (subseq (first (rest *days*)) 0 3)))
	 (format t "~D " print-attr)
	 (print-days (rest (rest *days*)) :l "short"))))

But you can see that each time the argument to PRINT-DAYS is getting
bigger, not smaller, and it will continue to grow without bounds, so
this expansion process will not terminate.

In order to taper things off, you need to know the value of variables
that will not be available  until runtime.  Consequently, you will not
be able to do this computation at defmacro time.  

You might succeed if the argument was a constant literal. e.g.,

 (print-days ("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday"
              "Friday" "Saturday") :l "short")

A definition like:

(defmacro print-days (d &key l)
  (if (null d)
      `nil
    (let ((print-attr (first d)))
      (if (equal l "short") 
          (setf print-attr (subseq (first d) 0 3)))
      `(progn (format t "~D " ,print-attr)
	      (print-days ,(rest d) :l ,l)))))


would work.  (Did for me anyway.)  but note that if you start with

 (print-days ("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday"
              "Friday" "Saturday) :l "short")


you'll get out:

 (progn (format t "~D " "Sun")
         (print-days ("Monday" "Tuesday"  "Wednesday" "Thursday"
                      "Friday" "Saturday) :l "short"))

THen you have to expand the PRINT-DAYS.  But fortunately, now, the 
expression is getting smaller rather than larger.  And it has a termination
condition so it should work.

Btw, ~D is an odd format directive to print symbolic days of week.
It does like ~A with a non-number arg, but is intended for printing numbers.

Also, name your keywords better.  Don't use :l.  The purpose of keywords
is to be readable and mnemonic.  Use &key style and :style.

Also, the thing you pass as the value of the keyword should be a keyword,
not a string, and then can be compared with EQL instead of EQUAL.

Good luck.