common-lispsbclclosansi-common-lisp

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


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?


Solution

  • 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)