Order of placement in package matters?

Discussion of Common Lisp
Post Reply
Harnon
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Order of placement in package matters?

Post by Harnon » Fri Aug 01, 2008 9:04 pm

Hello again!
I keep getting a strange (too me) error:
Funcall:appendf not a function, but a macro
Appendf is a define-modify-macro macro
The point is that this error is fixed by placing the macro before its usage in the package its defined and exported in.
So, if I use it in a function named finden (defined in same package), i have to place the macro definition before in order for everything to work. Otherwise, the error is produced.
What's happening?

qbg
Posts: 64
Joined: Mon Jun 30, 2008 1:05 pm
Location: Minnesota

Re: Order of placement in package matters?

Post by qbg » Fri Aug 01, 2008 9:53 pm

Assuming you have copied the appendf code from DEFINE-MODIFY-MACRO's CLHS page, could you post the code for finden? Does finden contain the code that is trying to FUNCALL appendf?

Kompottkin
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany
Contact:

Re: Order of placement in package matters?

Post by Kompottkin » Sat Aug 02, 2008 4:05 am

Harnon wrote:Hello again!
Hi.

First of all, packages do not contain macros or functions but symbols and symbols only. Therefore, you don't define macros or functions in packages but in files. See http://www.flownet.com/ron/packages.pdf for details. Now, on to the main point...
Harnon wrote:I keep getting a strange (too me) error:
Funcall:appendf not a function, but a macro
This indicates that the call to APPENDF has been compiled as a function call. This is to be expected if the macro was not defined at the time the call was compiled, because macros are run at compile-time (macroexpansion time, actually, but let's just ignore the difference for now), which is generally distinct from run-time (unless you're using a pure interpreter, but even then they may be distinct).

Let's say you COMPILE-FILE a file containing the following:

Code: Select all

(eval-when (:compile-toplevel)
  (format t "~%; Compiling DEBUG-STUFF."))

(defmacro debug-stuff (thing)
  (format t "~%; Expanding ~A." thing)
  (format t "~%; Returning ~A." `(print ,thing))
  `(print ,thing))

(eval-when (:compile-toplevel)
  (format t "~%; Compiling MULK."))

(defun mulk ()
  (debug-stuff (* (debug-stuff (+ 1 2)) 100))

(eval-when (:compile-toplevel)
  (format t "~%; Done compiling."))
During COMPILE-FILE, you're going to get the following messages:

Code: Select all

; Compiling DEBUG-STUFF.
; Compiling MULK.
; Expanding (* (DEBUG-STUFF (+ 1 2)) 100).
; Returning (PRINT (* (DEBUG-STUFF (+ 1 2)) 100).
; Expanding (+ 1 2).
; Returning (PRINT (+ 1 2)).
; Done compiling.
This indicates that DEBUG-STUFF has been called twice during the compilation of MULK and its output substituted for the original expressions:

Code: Select all

(defun mulk ()
  (print (* (print (+ 1 2)) 100))
The remaining forms are all function calls, so that the definition of MULK that is now dumped into the compiled file might look somewhat like this:

Code: Select all

(defun mulk ()
  (funcall 'print (funcall '* (funcall 'print (funcall '+ 1 2)) 100))
Now try LOADing the compiled file and executing MULK:

Code: Select all

> (mulk)
3
300
300
As you can see, DEBUG-STUFF is not called by MULK because it has already been macroexpanded away by the compiler.

In contrast, what happens when you COMPILE-FILE the following file?

Code: Select all

(eval-when (:compile-toplevel)
  (format t "~%; Compiling MULK."))

(defun mulk ()
  (debug-stuff (* (debug-stuff (+ 1 2)) 100))

(eval-when (:compile-toplevel)
  (format t "~%; Compiling DEBUG-STUFF."))

(defmacro debug-stuff (thing)
  (format t "~%; Expanding ~A." thing)
  (format t "~%; Returning ~A." `(print ,thing))
  `(print ,thing))

(eval-when (:compile-toplevel)
  (format t "~%; Done compiling."))
Obviously, at the time MULK is compiled, DEBUG-STUFF is not yet known. So the compiler assumes DEBUG-STUFF to be a function and compiles MULK accordingly:

Code: Select all

(defun mulk ()
  (funcall 'debug-stuff (funcall '* (funcall 'debug-stuff (funcall '+ 1 2)) 100))
When you try to call MULK now, the function DEBUG-STUFF is looked for and not found (because there is no such function).

findinglisp
Posts: 447
Joined: Sat Jun 28, 2008 7:49 am
Location: Austin, TX
Contact:

Re: Order of placement in package matters?

Post by findinglisp » Sat Aug 02, 2008 7:30 am

Nice explanation, Kompottkin. :)
Cheers, Dave
Slowly but surely the world is finding Lisp. http://www.findinglisp.com/blog/

Harnon
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Re: Order of placement in package matters?

Post by Harnon » Sat Aug 02, 2008 7:46 am

So basically i have to define all macros before they're used in functions if i want to package them?
Heres code;

Code: Select all

(defun finden (func lst)
  (loop for item in lst
	  for orig = nil then orig
        appending (if-let it (funcall func item) (progn (appendf orig (list item))
	                                                (list it))
                                          nil) into result
            finally (return (values result orig))))

Kompottkin
Posts: 94
Joined: Mon Jul 21, 2008 7:26 am
Location: München, Germany
Contact:

Re: Order of placement in package matters?

Post by Kompottkin » Sat Aug 02, 2008 8:03 am

findinglisp wrote:Nice explanation, Kompottkin. :)
Thank you! :)
Harnon wrote:So basically i have to define all macros before they're used in functions if i want to package them?
Yep, that's basically what follows from all the above. Macros need to be defined before any forms containing calls to them are compiled.

Harnon
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Re: Order of placement in package matters?

Post by Harnon » Sat Aug 02, 2008 10:46 pm

Thx guys!
Unfortunately, it appears like i have another package problem. :oops: See if you can figure this one out! :D

Error: When attempting to read the slot's values (slot-value), the slot reader-macro-commune::else is missing from the object #<IT.BESE.ARNESI:IF-FORM # {A9823A1}>.
The strange part is that the consequent and then form present no errors, but the else does.
when i replace (walker else) with (walker (slot-value form arnesi::else)), everything
works fine. Waz happenin?

Code: Select all

;;error occurs in defgenwalker if-form code
(defpackage :reader-macro-commune (:use :cl :arnesi :utilities))
(in-package :reader-macro-commune
(defgenwalker if-form (consequent then else)
              (make-instance 'if-form
                   :consequent (walker consequent)
                   :then (walker then)
                   :else (walker else)))


(defmacro defwalker (generic class (&rest slots) &body body)
	
  `(defmethod ,generic ((form ,class))
	(with-slots ,slots form
              ,@body)))

(defmacro defgenwalker (class (&rest slots) &body body)
  `(defwalker walker ,class (,@slots) ,@body))

Post Reply