Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

SBCL error "don't know how to dump [object]"

151 views
Skip to first unread message

Thomas Bartscher

unread,
Mar 16, 2011, 6:41:09 AM3/16/11
to
I don't know where else to turn and although I haven't seen anyone
else asking for help around here I assume it is okay to do so.

I have defined the macro def-collection this way:
(defmacro def-collection (&rest field-defs)
(let ((def-collection (make-def-collection)))
`(progn (loop for x in ',field-defs
do (progn
(add-def ,def-collection (car x)
(let ((definition (first (cdr x))))
(if (or (consp definition)
(symbolp definition))
(eval definition)
definition)))
(setf (fields ,def-collection)
(cons (car x)
(fields ,def-collection)))))
,def-collection)))

I now call this macro elsewhere like this:
(defun def-doc (session def-name &rest node-defs)
(declare (type session session)
(type symbol def-name))
(let ((node-def-collection (def-collection node-defs)))
(add-def (doc-defs session)
def-name node-def-collection)))

Upon loading the system containing the file containing the above
definition of def-doc I get the following error:
; compiling file "/home/thomas/Programme/Eigene/Lisp/MindNet/doc/
doc.lisp" (written 16 MAR 2011 11:22:55 AM):
; compiling (DEFPACKAGE :MINDNET-DOC ...)
; compiling (IN-PACKAGE :MINDNET-DOC)
; compiling (DEFCLASS DOCUMENT ...)
; compiling (DEFUN MAKE-DOCUMENT ...)
; compiling (DEFUN GET-NODE-DEF ...)
; compiling (DEFUN NODE-PRESENT ...)
; compiling (DEFUN DEF-DOC ...)

; file: /home/thomas/Programme/Eigene/Lisp/MindNet/doc/doc.lisp
; in: DEFUN DEF-DOC => DEF-COLLECTION NODE-DEFS
; (TB-DEF-COLLECTION:DEF-COLLECTION MINDNET-DOC::NODE-DEFS)
; --> PROGN LOOP BLOCK LET SB-LOOP::LOOP-BODY TAGBODY PROGN
; --> TB-DEF-COLLECTION:ADD-DEF
; ==>
; #<TB-DEF-COLLECTION:DEF-COLLECTION {100454ABC1}>
;
; caught ERROR:
; don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
MAKE-LOAD-FORM method called).

; --> PROGN LOOP BLOCK LET SB-LOOP::LOOP-BODY TAGBODY PROGN SETF LET*
; --> MULTIPLE-VALUE-BIND LET FUNCALL SB-C::%FUNCALL
; --> (SETF TB-DEF-COLLECTION::FIELDS)
; ==>
; #<TB-DEF-COLLECTION:DEF-COLLECTION {100454ABC1}>
;
; caught ERROR:
; don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
MAKE-LOAD-FORM method called).

; --> PROGN LOOP BLOCK LET SB-LOOP::LOOP-BODY TAGBODY PROGN SETF LET*
; --> MULTIPLE-VALUE-BIND LET CONS TB-DEF-COLLECTION::FIELDS
; ==>
; #<TB-DEF-COLLECTION:DEF-COLLECTION {100454ABC1}>
;
; caught ERROR:
; don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
MAKE-LOAD-FORM method called).

; --> PROGN
; ==>
; #<TB-DEF-COLLECTION:DEF-COLLECTION {100454ABC1}>
;
; caught ERROR:
; don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
MAKE-LOAD-FORM method called).

; in: DEFUN DEF-DOC
; (LET ((MINDNET-DOC::NODE-DEF-COLLECTION
; (TB-DEF-COLLECTION:DEF-COLLECTION MINDNET-DOC::NODE-
DEFS)))
; (TB-DEF-COLLECTION:ADD-DEF (MINDNET-DOC::DOC-DEFS MINDNET-
DOC::SESSION)
; MINDNET-DOC::DEF-NAME
; MINDNET-DOC::NODE-DEF-COLLECTION))
;
; note: deleting unreachable code
;
; note: deleting unreachable code

; /home/thomas/.cache/common-lisp/sbcl-1.0.46-linux-amd64/home/thomas/
Programme/Eigene/Lisp/MindNet/doc/ASDF-TMP-doc.fasl written
; compilation finished in 0:00:00.051

What is going on here?

Pascal J. Bourguignon

unread,
Mar 16, 2011, 6:50:57 AM3/16/11
to
Thomas Bartscher <thomas.b...@googlemail.com> writes:

> I don't know where else to turn and although I haven't seen anyone
> else asking for help around here I assume it is okay to do so.

Yes, I guess once you remove the kill-fileable trolls, there doesn't
remain a lot of question and answer posts. Thank you for increasing the
signal/noise ratio.

Vous pouvez aussi poser des questions sur news://fr.comp.lang.lisp

> I have defined the macro def-collection this way:
>
> (defmacro def-collection (&rest field-defs)
> (let ((def-collection (make-def-collection)))
> `(progn (loop for x in ',field-defs
> do (progn
> (add-def ,def-collection (car x)
> (let ((definition (first (cdr x))))
> (if (or (consp definition)
> (symbolp definition))
> (eval definition)
> definition)))
> (setf (fields ,def-collection)
> (cons (car x)
> (fields ,def-collection)))))
> ,def-collection)))
>
> I now call this macro elsewhere like this:
> (defun def-doc (session def-name &rest node-defs)
> (declare (type session session)
> (type symbol def-name))
> (let ((node-def-collection (def-collection node-defs)))
> (add-def (doc-defs session)
> def-name node-def-collection)))

This cannot work as is, since functions used by the macro (at
macroexpansion time) are not defined:


CL-USER> (defmacro def-collection (&rest field-defs)


(let ((def-collection (make-def-collection)))
`(progn (loop for x in ',field-defs
do (progn
(add-def ,def-collection (car x)
(let ((definition (first (cdr x))))
(if (or (consp definition)
(symbolp definition))
(eval definition)
definition)))
(setf (fields ,def-collection)
(cons (car x)
(fields ,def-collection)))))
,def-collection)))

DEF-COLLECTION
CL-USER> (macroexpand '(def-collection node-defs))

*** - EVAL: undefined function MAKE-DEF-COLLECTION


Also, since you don't use the macro as a toplevel form, it's name is not
too good. A DEFSOMETHING or DEFINE-SOMETHING (DEF-SOMETHING is also
"lexically" bad style) macro is expected to define something, like
DEFPACKAGE or DEFINE-CONDITION. If you want to make a new object, you
should rather provide a MAKE-SOMETHING function.


Without a definition for make-def-collection, we cannot really help with
your problem, apart giving generic advice, such as, given:

> ; don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
> MAKE-LOAD-FORM method called).

do not generate in the program source objects that cannot be serialized
into a .fasl file.


--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Thomas Bartscher

unread,
Mar 16, 2011, 7:08:11 AM3/16/11
to
Oh, this was fast!

On 16 Mrz., 11:50, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:

I am pretty sure that function definitions are available at
macroexpansion time as long as they are defined before the macro,
which is the case with all functions used by def-collection.

>
> Also, since you don't use the macro as a toplevel form, it's name is not
> too good.   A DEFSOMETHING or DEFINE-SOMETHING  (DEF-SOMETHING is also
> "lexically" bad style) macro is expected to define something, like
> DEFPACKAGE or DEFINE-CONDITION.  If you want to make a new object, you
> should rather provide a MAKE-SOMETHING function.

Will try to find a better name.

> Without a definition for make-def-collection, we cannot really help with
> your problem, apart giving generic advice, such as, given:

This is the code directly before the macro definition.
(defclass def-collection ()
((fields :type (or cons null)
:accessor fields
:initform '())
(field-defs :type hash-table
:accessor field-defs
:initform (make-hash-table :test 'eq))))

(defun make-def-collection ()
(make-instance 'def-collection))

(defgeneric add-def (object name def))

(defmethod add-def ((object def-collection)
(name symbol)
def)
(setf (gethash name (field-defs object))
(if def
def
(constantly t))))

> > ;   don't know how to dump #<DEF-COLLECTION {100454ABC1}> (default
> > MAKE-LOAD-FORM method called).
>
> do not generate in the program source objects that cannot be serialized
> into a .fasl file.
>
> --
> __Pascal Bourguignon__                    http://www.informatimago.com/
> A bad day in () is better than a good day in {}.

Unfortunately I didn't understand a word. Does this mean, SBCL doesn't
know how to write an object into a fasl automatically when the class
of that object is defined? How would I fix this?

Pascal J. Bourguignon

unread,
Mar 16, 2011, 7:48:24 AM3/16/11
to
Thomas Bartscher <thomas.b...@googlemail.com> writes:


>> CL-USER> (macroexpand '(def-collection node-defs))
>>
>> *** - EVAL: undefined function MAKE-DEF-COLLECTION
> I am pretty sure that function definitions are available at
> macroexpansion time as long as they are defined before the macro,
> which is the case with all functions used by def-collection.

Perhaps, but how can I know it?
How can I debug it if you don't provide it?


So, now, we can expand the macro, and see that:

CL-USER> (macroexpand '(def-collection node-defs))
(PROGN
(LOOP FOR X IN '(NODE-DEFS) DO
(PROGN
(ADD-DEF #<DEF-COLLECTION #x000334354F50> (CAR X)
(LET ((DEFINITION (FIRST (CDR X))))
(IF (OR (CONSP DEFINITION) (SYMBOLP DEFINITION)) (EVAL DEFINITION)
DEFINITION)))
(SETF (FIELDS #<DEF-COLLECTION #x000334354F50>)
(CONS (CAR X) (FIELDS #<DEF-COLLECTION #x000334354F50>)))))
#<DEF-COLLECTION #x000334354F50>)
T
CL-USER>

the source code generated, contains objects that are not printable
readably (those #<DEF-COLLECTION ...> objects). When you compile the
file, these objects need to be stored in the object file (the .fasl
file).


You have two solutions:

1- provide a MAKE-LOAD-FORM method for this class of object

2- generate instead code instantiating those objects at run-time,
instead of at compilation time.


My advice would be to choose option 2, which is simplier, and given that
the DEF-COLLECTION macro fills the object at run-time, there's no point
in creating the object at compilation time.

But the first problem you have is that def-collection should not be a
macro, as I said in the previous answer, since you are not passing to
the macro the field definitions, but a symbol (node-defs). The symptom
is your use of EVAL. This cannot work.

I would also advice to find another name. You're dealing with collections of "def"
whatever they are. If you could find another name, perhaps the name
in-extenso is not "definition", then use it in-extenso, otherwise use a
synonym, so that you can avoid writing DEF-COLLECTION, and confounding
it with a defining macro.


So, the first solution would be to forget about the def-collection
macro, and just write a function:

(defun make-def-collection (&rest field-defs &key &allow-other-keys)
(loop
:with c = (make-instance 'def-collection)
:for (key value) :on field-defs :by (function cddr)
:do (add-def c key value)
:finally (return c)))


(defmethod print-object ((self def-collection) stream)
(print-unreadable-object (self stream :identity t :type t)
(prin1 (list :fields (fields self)
:field-defs (ALEXANDRIA:HASH-TABLE-ALIST (field-defs self)))
stream))
self)


You can use it in DEF-DOC as follow:

(defun def-doc (session def-name &rest node-defs &key &allow-other-keys)


(declare (type session session)
(type symbol def-name))

(let ((node-def-collection (apply (function make-def-collection) node-defs)))


(add-def (doc-defs session)
def-name node-def-collection)))

C/USER[21]> (make-def-collection
:name 'c
:type 'def-collection
:description "A def-collection describing the variable C")
#<DEF-COLLECTION (:FIELDS NIL
:FIELD-DEFS ((:NAME . C)
(:TYPE . DEF-COLLECTION)
(:DESCRIPTION . "A def-collection describing the variable C")))
#x000334412280>

Now we see that there's is another problem, an encapsulation problem,
with your def-collection macro, and the ADD-DEF method. You want
obviously to keep in fields the name of the entries in the field-defs
hash-table.

Notice first that you can trivially collect this list with:

(let ((keys '()))
(maphash (lambda (k v) (declare (ignore v)) (push k keys))
(field-defs c)))

or:

(loop for key being the keys in (field-defs c) collect key)

or:

(alexandria:hash-table-keys (field-defs c))

Notice also that most operations you may have to do on field names will
be done much faster on the hash table than on the list of field names
(insertion, since it's already done in the hash-table), suppression or
detection (both O(n) in the list, O(1) in the hash-table).

But assuming you really need to have this list, then it should obviously
be maintained by the methods modifying the hash-table, such as ADD-DEF:

(defmethod add-def ((object def-collection)
(name symbol)
def)

(pushnew name (fields object))


(setf (gethash name (field-defs object))
(if def
def
(constantly t))))

instead of in code external to the object.
CL-USER> (make-def-collection
:name 'c
:type 'def-collection
:description "A def-collection describing the variable C")
#<DEF-COLLECTION (:FIELDS (:DESCRIPTION :TYPE :NAME)
:FIELD-DEFS ((:NAME . C)
(:TYPE . DEF-COLLECTION)
(:DESCRIPTION . "A def-collection
describing the variable C")))
#x00033E5B2460>

A final advice: you should add documentation strings to your definitions
(defclass, defgeneric, defmethod, defun, defmacro, etc).

Mario S. Mommer

unread,
Mar 16, 2011, 8:20:46 AM3/16/11
to

The problem you are having is due to the fact your the macro not only
creates code, but that this code has actual live objects embedded into
them in this case. That's the def-collection thing. That is not
necessarily a problem, but in this case it is because sbcl does not know
how to save such an object in a fasl. You can either figure out how to
make an appropriate load form, or do something that avoids the use of
the actual live object.

Thomas Bartscher

unread,
Mar 16, 2011, 8:59:54 AM3/16/11
to
On 16 Mrz., 12:48, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:

Thank you very much, I could solve my problems with your help.
There is just one problem remaining: Supplying the definitions for a
collection of definitions is kinda tedious now.
The "defs" rest arguments are now supplied in the form
(make-def-collection `(text ,#'stringp))
(as make-def-collection is a function now) which seems kinda tedious.
So to make the syntax more pleasing so I can write
(make-def-collection (text #'stringp))
with #'stringp being evaluated and text not I still will have to rely
on a macro and a call to eval in that macro - or is there another,
cleaner way?

The encapsulation problem I noted right after posting that last mail,
thanks for pointing it out!

Pascal J. Bourguignon

unread,
Mar 16, 2011, 9:36:33 AM3/16/11
to
Thomas Bartscher <thomas.b...@googlemail.com> writes:

>> (defun make-def-collection (&rest field-defs &key &allow-other-keys)
>> (loop
>> :with c = (make-instance 'def-collection)
>> :for (key value) :on field-defs :by (function cddr)
>> :do (add-def c key value)
>> :finally (return c)))
>>

>> CL-USER> (make-def-collection
>> :name 'c
>> :type 'def-collection
>> :description "A def-collection describing the variable C")
>

> Thank you very much, I could solve my problems with your help.
> There is just one problem remaining: Supplying the definitions for a
> collection of definitions is kinda tedious now.
> The "defs" rest arguments are now supplied in the form
> (make-def-collection `(text ,#'stringp))
> (as make-def-collection is a function now) which seems kinda tedious.
> So to make the syntax more pleasing so I can write
> (make-def-collection (text #'stringp))
> with #'stringp being evaluated and text not I still will have to rely
> on a macro and a call to eval in that macro - or is there another,
> cleaner way?

The idea of &rest &key &allow-other-keys is to let you drop the
parentheses. What it implies on the other hand, is that a value be
given for each "key". See the call to make-def-collection given as
example above. Notice that the keywords don't need to be KEYWORDs, they
can be symbols, as you specified for your ADD-DEF method. So you can
write:


(make-def-collection 'text (function stringp)
'value (function numberp))

if you want. (or:

(make-def-collection 'text #'stringp
'value #'numberp)

if you insist).


We could define a macro wrapper to avoid quoting, eg:

(defmacro build-def-collection (&rest args &key &allow-other-keys)
`(make-def-collection ,@(loop :for (key value) on args by (function cddr)
:collect `(quote ,key)
:collect value)))

so tha we could write:

(make-def-collection text (function stringp)
value (function numberp))

and something similar could be done if we kept the key value in their
own lists, but it's of little value and use. Notice for example, how
you want to use MAKE-DEF-COLLECTION from the DEF-DOC function, you
cannot use a macro in there, because the parameters are not know at
compilation time, but at run-time, held in the NODE-DEFS variable.

Finally, if you are using those functions to specify the type of some
values, notice that in Common Lisp (contrarily to say, Scheme), we have
type specifiers. So instead of a function, you could pass sexps
denoting types:

(make-def-collection 'text 'string
'value 'number
'export '(vector (unsigned-byte 8) 4))

then you can check that a value is of a given type with:

(typep value type)


You can still use a random predicate to specify a type with:

(deftype arbitary-type () '(satisfies arbitrary-predicate-p))

if needed.


One advantage of type specifiers, is that they can be serialized and
deserialized by the standard PRINT and READ functions, while functions
cannot.

Thomas Bartscher

unread,
Mar 16, 2011, 10:16:33 AM3/16/11
to
On 16 Mrz., 14:36, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:

This makes sense and looks nice, too. Although I still don't
understand why I should use &key &allow-other-keys. make-field-
collection (former make-def-collection) seems to work fine without
those.

Pascal J. Bourguignon

unread,
Mar 16, 2011, 12:44:12 PM3/16/11
to
Thomas Bartscher <thomas.b...@googlemail.com> writes:

> This makes sense and looks nice, too. Although I still don't
> understand why I should use &key &allow-other-keys. make-field-
> collection (former make-def-collection) seems to work fine without
> those.

This is to impose the keyword/value structure of the arguments:

C/USER[22]> (defun f (&rest args) (print (list (length args) args)))
F
C/USER[23]> (defun g (&rest args &key &allow-other-keys) (print (list (length args) args)))
G
C/USER[24]> (f 1 2 3)

(3 (1 2 3))
(3 (1 2 3))
C/USER[25]> (g 1 2 3)

*** - G: keyword arguments in (1 2 3) should occur pairwise
The following restarts are available:
ABORT :R1 Abort main loop
C/Break 1 USER[26]> :q
C/USER[27]> (g 1 2 3 4)

*** - G: &KEY marker 1 is not a symbol
The following restarts are available:
ABORT :R1 Abort main loop
C/Break 1 USER[28]> :q
C/USER[29]>

Rob Warnock

unread,
Mar 16, 2011, 10:02:17 PM3/16/11
to
Thomas Bartscher <thomas.b...@googlemail.com> wrote:
+---------------

| "Pascal J. Bourguignon" <p...@informatimago.com> wrote:
| > This cannot work as is, since functions used by the macro (at
| > macroexpansion time) are not defined:
...

| > CL-USER> (macroexpand '(def-collection node-defs))
| >
| > *** - EVAL: undefined function MAKE-DEF-COLLECTION
|
| I am pretty sure that function definitions are available at
| macroexpansion time as long as they are defined before the macro...
+---------------

That is *NOT* a good assumption!! It might be true for code loaded one
form at a time into an interpreter, but it's *NOT* going to be true for
COMPILE-FILE in general, since functions defined in the file will not be
"available" until the compiled FASL is LOADed, which is far too late to
help with macroexpansion! ;-}

You can fix this by wrapping all definitions of macro helper functions
[and any functions *they* call, etc.] in an EVAL-WHEN, e.g.:

(eval-when (:compile-toplevel :load-toplevel :execute)
(defun helper1 (args...)
...whatever...)
(defun helper2 (args...)
...(helper1 arg1 arg2)...))

(defmacro my-mac (args...)
`(this that ,@(helper2 (first args) bar) someother))

+---------------


| which is the case with all functions used by def-collection.

+---------------

Except, as Pascal observed, you didn't show that they'd been defined,
nor that theyd been defined inside the proper EVAL-WHEN.

+---------------


| > do not generate in the program source objects that cannot be serialized
| > into a .fasl file.
|

| Unfortunately I didn't understand a word. Does this mean, SBCL doesn't
| know how to write an object into a fasl automatically when the class
| of that object is defined?

+---------------

The set of objects that can be reliably & portably externalized
(serialized) into compiled (FASL) files is limited, see:

http://www.lispworks.com/documentation/HyperSpec/Body/03_bd.htm
3.2.4 Literal Objects in Compiled Files

and *all* the sub-sections thereof. In general, only "literal" objects
are externalizable, that is:

http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#literal
...
literal adj. (of an object) referenced directly in a program rather
than being computed by the program; that is, appearing as data in a
quote form, or, if the object is a self-evaluating object, appearing
as unquoted data. ...

and among those, only the ones that are capable of being "similar":

http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#similar
similar adj. (of two objects) defined to be equivalent under the
similarity relationship.

similarity n. a two-place conceptual equivalence predicate, which
is independent of the Lisp image so that two objects in different
Lisp images can be understood to be equivalent under this predicate.
See Section 3.2.4 (Literal Objects in Compiled Files).

http://www.lispworks.com/documentation/HyperSpec/Body/03_bdbb.htm
3.2.4.2.2 Definition of Similarity

Two objects S (in source code) and C (in compiled code) are defined
to be similar if and only if they are both of one of the types listed
here (or defined by the implementation) and they both satisfy all
additional requirements of similarity indicated for that type.
...
structure-object and standard-object

A general-purpose concept of similarity does not exist for
structures and standard objects. However, a conforming program is
permitted to define a make-load-form method for any class K
defined by that program that is a subclass of either
structure-object or standard-object. The effect of such a method
is to define that an object S of type K in source code is similar
to an object C of type K in compiled code if C was constructed
from code produced by calling make-load-form on S.

+---------------


| How would I fix this?

+---------------

You would need to define a MAKE-LOAD-FORM method for your class(es).


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <http://rpw3.org/>
San Mateo, CA 94403

0 new messages