2
votes

either I'm missing something very stupid or the scope of special variables is unexpectedly different for defmethod and defun (tested using SBCL 1.1.14):

  1. As expected:

    (defun ttprint-object (prefix out)
      (format out "~A: in defun: < ~A >~%" prefix *print-readably*))
    (let ((*print-readably* t))
      (format t "let: calling defun: < ~A >~%" *print-readably*)
      (ttprint-object "from let" t))
    
    let: calling defun: < T >
    from let: in defun: < T >
    
  2. With let defmethod works different from defun, so unexpected:

    (defclass empty () ())
    
    (defmethod print-object ((self empty) out)
      (format out "in defmethod: < ~A >~%" *print-readably*)
      (ttprint-object "from defmethod" out))
    
    (let ((*print-readably* t))
      (ttprint-object "from let" t)
      (format t "let: calling defmethod: < ~A >~%" *print-readably*)
      (format t "let: ~A" (make-instance 'empty)))
    
    from let: in defun: < T >
    let: calling defmethod: < T >
    let: in defmethod: < NIL >
    from defmethod: in defun: < NIL >
    
  3. Also with setf defmethod works different from defun but same as with let:

    (progn
      (setq *print-readably* t)
      (ttprint-object "from setf" t)
      (format t "setf: calling defmethod: < ~A >~%" *print-readably*)
      (format t "setf: ~A" (make-instance 'empty)))
    
    from setf: in defun: < T >
    setf: calling defmethod: < T >
    setf: in defmethod: < NIL >
    from defmethod: in defun: < NIL >
    

Hopefully it's me...

Thanks in advance, Frank

1

1 Answers

8
votes

~A binds *print-readably* to false (emphasis added):

22.3.4.1 Tilde A: Aesthetic

An arg, any object, is printed without escape characters (as by princ). If arg is a string, its characters will be output verbatim. If arg is nil it will be printed as nil; the colon modifier (~:A) will cause an arg of nil to be printed as (), but if arg is a composite structure, such as a list or vector, any contained occurrences of nil will still be printed as nil.

~A binds *print-escape* to false, and *print-readably* to false.

When you do

(format t "let: ~A" (make-instance 'empty))

the ~A directive binds *print-readably* to false (i.e., nil), and eventually the Lisp writer calls the print-object method for the object. If you don't want this binding, you might try ~W which doesn't modify the printer variables:

22.3.4.3 Tilde W: Write

An argument, any object, is printed obeying every printer control variable (as by write). In addition, ~W interacts correctly with depth abbreviation, by not resetting the depth counter to zero. ~W does not accept parameters. If given the colon modifier, ~W binds *print-pretty* to true. If given the at-sign modifier, ~W binds *print-level* and *print-length* to nil.

~W provides automatic support for the detection of circularity and sharing. If the value of *print-circle* is not nil and ~W is applied to an argument that is a circular (or shared) reference, an appropriate #n# marker is inserted in the output instead of printing the argument.

If you use ~W, you get the results you'd originally expected:

CL-USER> (let ((*print-readably* t))
               (ttprint-object "from let" t)
               (format t "let: calling defmethod: < ~A >~%" *print-readably*)
               (format t "let: ~w" (make-instance 'empty)))
    from let: in defun: < T >
    let: calling defmethod: < T >
    let: in defmethod: < T >
    from defmethod: in defun: < T >