Redefinition of the print-object method for conses has different effects in different CL implementations

386 views Asked by At

Trying to print conses not in standard list notation, but always as dotted pairs, with the minimum effort, I have redefined the method print-object in this way:

(defmethod print-object((c cons) str)
  (format str "(~a . ~a)" (car c) (cdr c)))

but the effect is different for different implementations.

In Clozure CL and in LispWorks Personal the result is what I was expecting:

CL-USER 1 > (defmethod print-object((c cons) str)
                (format str "(~a . ~a)" (car c) (cdr c)))
#<STANDARD-METHOD PRINT-OBJECT NIL (CONS . (T . NIL)) 200A45AB>

CL-USER 2 > '(a b c )
(A . (B . (C . NIL)))

while in SBCL and AllegroCLexpress nothing change in the way lists are printed:

* (defmethod print-object((c cons) str)
     (format str "(~a . ~a)" (car c) (cdr c)))

#<STANDARD-METHOD PRINT-OBJECT (CONS T) {10051EBC53}>
* '(a b c)

(A B C)

So, I am wondering if this is due to some ambiguity in the specification of the language, if such behaviour is explicitly declared as unspecified, if this is due to some interaction of the REPL with the packages, or, finally, if there are implementations that are correct with respect to this definition and others which are incorrect. As final note, giving such definition inside SLIME causes total havoc of SLIME itself!

Can somebody shed some light over such differencies, and also suggest an alternative way, if it exists, less problematic, of obtaining my objective?

2

There are 2 answers

1
m-n On BEST ANSWER

CLHS 11.1.2.1.2 lists constraints on changing things in the common lisp package -- this seems to run afoul of situation 19.

Except where explicitly allowed, the consequences are undefined if any of the following actions are performed on an external symbol of the COMMON-LISP package:

...

  1. Defining a method for a standardized generic function which is applicable when all of the arguments are direct instances of standardized classes.

A partial workaround, just for when *print-pretty* is non-nil, would be to use a pretty print dispatch table.

;(in-package cl-user)
(set-pprint-dispatch 'cons (lambda (s obj) (format s "(~A . ~A)" (car obj) (cdr obj))))
NIL
;(in-package cl-user)
(let ((*print-pretty* t)) (print (list 1 2 3)))

(1 . (2 . (3 . NIL)))
(1 2 3)
;(in-package cl-user)
1
Rainer Joswig On

The title is slightly misleading. SBCL does not have a print-object method for conses. So you can't redefine it. For some reason it lets you define such a method, but its printer won't call it.

AFAIK, SBCL does not support user-defined print-object methods on built-in types. They are not called by the printer. If you look at the printer sources, it is using a TYPE-CASE construct, to call various printing functions for different data types.

You can define print-object methods for CLOS classes in SBCL and they will be called.

Is this incompatible with ANSI CL? Maybe. At least the part of not providing print-object methods should be incompatible. This would still not mean one could change them...

Would it be useful to have printing for all/most/many classes (including builtin-classes) being defined in terms of PRINT-OBJECT methods. Yes.