From: dpapathanasiou
Subject: Compiling macros with an optional function parameter?
Date: 
Message-ID: <161c110a-8ea2-4f17-bf82-fe4157d3cf3e@k37g2000hsf.googlegroups.com>
I'm not sure if this is a Lisp thing or an issue specific to CMUCL,
but when I compile this macro and try to include the resulting x86f
file into a larger binary (i.e. using 'cat'), I see this: "Error in
batch processing: Cannot dump objects of type FUNCTION into fasl
files."

This is the macro:

(defmacro update-list-hash (hash data-key data-value &optional (test-
fn #'string-equal))
  "Update the given hash (key:value is 1:n) with data by adding data
to the list of values for the key."
  `(let ((current (gethash ,data-key ,hash)))
    (if (member ,data-value current :test ,test-fn)
        current
        (setf (gethash ,data-key ,hash) (cons ,data-value current)))))

If I remove the optional parameter and explicitly specify the
member :test parameter inside the macro, I can compile and cat the
resulting x86f file without any problems.

Any idea why this might be happening and what (if any) work-arounds
there are?

I'm using CMUCL 19e on debian etch.

From: Pascal J. Bourguignon
Subject: Re: Compiling macros with an optional function parameter?
Date: 
Message-ID: <874p458m2q.fsf@hubble.informatimago.com>
dpapathanasiou <···················@gmail.com> writes:

> I'm not sure if this is a Lisp thing or an issue specific to CMUCL,
> but when I compile this macro and try to include the resulting x86f
> file into a larger binary (i.e. using 'cat'), I see this: "Error in
> batch processing: Cannot dump objects of type FUNCTION into fasl
> files."
>
> This is the macro:
>
> (defmacro update-list-hash (hash data-key data-value &optional (test-
> fn #'string-equal))
>   "Update the given hash (key:value is 1:n) with data by adding data
> to the list of values for the key."
>   `(let ((current (gethash ,data-key ,hash)))
>     (if (member ,data-value current :test ,test-fn)
>         current
>         (setf (gethash ,data-key ,hash) (cons ,data-value current)))))

Your macro is very very bad, because it evaluates several times its arguments.

Try to guess in what hash table it will put the values!

(let ((i 0)
      (hashes (vector (make-hash-table) (make-hash-table) 
                      (make-hash-table) (make-hash-table)
                      (make-hash-table) (make-hash-table)))
      (k 100)
      (v 200))
  (update-list-hash (aref hashes (incf i))
                    (incf k) (format nil "~A" (incf v)))
  (update-list-hash (aref hashes (incf i))
                    (incf k) (format nil "~A" (incf v)))
  hashes)




-->
#(#S(HASH-TABLE :TEST EXT:FASTHASH-EQL) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL)
  #S(HASH-TABLE :TEST EXT:FASTHASH-EQL (102 . ("202"))) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL)
  #S(HASH-TABLE :TEST EXT:FASTHASH-EQL (104 . ("204"))) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL))


You should either use temporary variables, (whose name is generated by
GENSYM), or GET-SETF-EXPANSION.

But in this case, you don't need GET-SETF-EXPANSION, which is a sign
that you don't need a macro at all: just write a function!



> If I remove the optional parameter and explicitly specify the
> member :test parameter inside the macro, I can compile and cat the
> resulting x86f file without any problems.

The problem is that the default values for arguments are evaluated at
compilation time.  Thus test-fn default is a function, and a function
cannot be serialized into the .x86f file.

You could quote it, which would defer the evaluation of (function
string=) to run-time:

(defmacro mf (n &optional (f (function sin)))
  `(funcall ,f ,n))

(defmacro mq (n &optional (f '(function sin)))
  `(funcall ,f ,n))


C/USER[76]> (macroexpand '(mf pi))
(FUNCALL #<SYSTEM-FUNCTION SIN> PI) ;
T
C/USER[77]> (macroexpand '(mq pi))
(FUNCALL #'SIN PI) ;
T
C/USER[78]>



> Any idea why this might be happening and what (if any) work-arounds
> there are?

What about:

   (pushnew data-value (gethash data-key hash '())
            :test (function string=))

?



Or, if you need to keep the order of evaluation:

(defun update-list-hash (hash data-key data-value &optional (test #'string-equal))
   (pushnew data-value (gethash data-key hash '())
            :test (function string=)))

so you can write:

(let ((i 0)
      (hashes (vector (make-hash-table) (make-hash-table) 
                      (make-hash-table) (make-hash-table)
                      (make-hash-table) (make-hash-table)))
      (k 100)
      (v 200))
  (update-list-hash (aref hashes (incf i))
                    (incf k) (format nil "~A" (incf v)))
  (update-list-hash (aref hashes (incf i))
                    (incf k) (format nil "~A" (incf v)))
  hashes)
-->
#(#S(HASH-TABLE :TEST EXT:FASTHASH-EQL) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL (101 . ("201")))
  #S(HASH-TABLE :TEST EXT:FASTHASH-EQL (102 . ("202"))) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL)
  #S(HASH-TABLE :TEST EXT:FASTHASH-EQL) #S(HASH-TABLE :TEST EXT:FASTHASH-EQL))


> I'm using CMUCL 19e on debian etch.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

This is a signature virus.  Add me to your signature and help me to live.
From: dpapathanasiou
Subject: Re: Compiling macros with an optional function parameter?
Date: 
Message-ID: <8c7749d1-5884-4a80-83fb-ba121f2f4a40@d1g2000hsg.googlegroups.com>
On Sep 24, 1:56 pm, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> You should either use temporary variables, (whose name is generated by
> GENSYM), or GET-SETF-EXPANSION.

Ah, thanks for reminding me about gensym; while I'm not using the
macro in a context likely to create trouble, there is not reason not
to use it.

> The problem is that the default values for arguments are evaluated at
> compilation time.  Thus test-fn default is a function, and a function
> cannot be serialized into the .x86f file.

Oddly enough, CMUCL *did* compile the macro without any warnings or
errors, and I could invoke the macro from the compiled form without a
problem.

It was only when I tried to concatenate the compiled form when I got
the error.

> You could quote it, which would defer the evaluation of (function
> string=) to run-time:

Yes, that solved it.