Frank Ruben
Frank Ruben

Reputation: 105

Common Lisp special variable scope different for defmethod and defun?

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

Upvotes: 2

Views: 270

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85813

~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 >

Upvotes: 8

Related Questions