Page 1 of 1

Order of placement in package matters?

PostPosted: Fri Aug 01, 2008 9:04 pm
by Harnon
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?

Re: Order of placement in package matters?

PostPosted: Fri Aug 01, 2008 9:53 pm
by qbg
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?

Re: Order of placement in package matters?

PostPosted: Sat Aug 02, 2008 4:05 am
by Kompottkin
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).

Re: Order of placement in package matters?

PostPosted: Sat Aug 02, 2008 7:30 am
by findinglisp
Nice explanation, Kompottkin. :)

Re: Order of placement in package matters?

PostPosted: Sat Aug 02, 2008 7:46 am
by Harnon
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))))

Re: Order of placement in package matters?

PostPosted: Sat Aug 02, 2008 8:03 am
by Kompottkin
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.

Re: Order of placement in package matters?

PostPosted: Sat Aug 02, 2008 10:46 pm
by Harnon
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))