Hi everybody!
With the code at the end of this mail I would have expected an outcome similar to this one
R: T , E: T - [REFUSED TO PRINT] R: T , E: NIL - [REFUSED TO PRINT] R: NIL, E: T - #<MY-CONDITION 20098B07> R: NIL, E: NIL - My condition occured.
because the standard says that "if *PRINT-READABLY* is true, printing proceeds as if *PRINT-ESCAPE* were also true." This would mean that the first two lines should look the same because the value of *PRINT-ESCAPE* is irrelevant in this case. However, while this is what happens for example with LispWorks, AllegroCL, and CLISP, in some other Lisps like SBCL, CCL, and ECL the second line looks like the fourth.
Isn't that wrong? Or is their interpretation of the standard that the report function is responsible for obeying *PRINT-READABLY*? But how does that fit with the possibility of using strings for :REPORT?
Thanks, Edi.
(define-condition my-condition () () (:report "My condition occured."))
(defun test () (let ((error (make-condition 'my-condition))) (dolist (readably '(t nil)) (dolist (escape '(t nil)) (format t "~&R: ~3A, E: ~3A - " readably escape) (handler-case (write error :readably readably :escape escape) (print-not-readable () (write-string "[REFUSED TO PRINT]")))))))
The dictionary page for *print-readably* says
Individual *methods http://franz.com/support/documentation/current/ansicl/glossary/m.htm#method* for *print-object* http://franz.com/support/documentation/current/ansicl/dictentr/print-ob.htm, including user-defined *methods http://franz.com/support/documentation/current/ansicl/glossary/m.htm#method*, are responsible for implementing these requirements.
so the implementation's applicable print-object method for class condition violates this requirement.
The explanation of *print-readably* unwritten in the ANS is that it's purpose is to provide a guarantee that something written will later be rereadable without error, producing an object that is the "same" as the original. (The definition of "same" is a different Lisp execution complicated, but not the central focus here.) Mostly *print-readably* guarantees that the emitted sequence of character can be reread without signalling error.
All this depends on a bunch of other assumptions, and the necessary details are incomplete in the ANS. Here are just some of the issues:
What about *print-pretty* and dispatches defined in the current pprint-dispatch? If there is some such dispatch that would violate rereadability, should the printer ignore that *print-pretty* is true? A naive reading of the standard suggests this is a requirement (despite being operationally ridiculous) but perhaps the intention was that *print-readably* ought just bind *print-pretty* false. But if so, the ANS would have said so.
What about other printer variables? One might define a print-object method that assumes that *print-base* will be the same at reread time -- but the ANS doesn't say this.
Perhaps the ANS missed requiring that *print-readably* can assume the emitted sexpr will be reread inside a with-standard-io-syntax? But the ANS doesn't say this.
What about the package system? Whether the printed representation of a symbol has zero, one, or two colons depends on the current state of the package system. Should *print-readably* assume the package system will be the same at read time? Perhaps, but the ANS doesn't say this. And with-standard-io-syntax doesn't help with this.
There are a great many more potential issues. We must conclude that *print-readably* is only a hook for user code to provide a _weak_ guarantee that no _other_ user code will do things that prevent printed output from being rereadable without likely notification at write time. But to achieve even this weak guarantee, user code must cooperate with what it knows about the printer and reader. That it applies to the printer representation of conditions might protect a mostly-numeric output file from containing an unreadable #<floating-point-underflow> object where a float is expected.
On 07/09/15 03:47, Steve Haflich wrote:
The dictionary page for *print-readably* says
Individual methods for print-object, including user-defined methods, are responsible for implementing these requirements.
so the implementation's applicable print-object method for class condition violates this requirement.
I don't see how:
$ clall -r '(let ((*print-readably* t)) (prin1-to-string (make-condition (quote condition))))'
Armed Bear Common Lisp #<CONDITION {712BEB60}> cannot be printed readably. Clozure Common Lisp Attempt to print object #<CONDITION #x302000568F2D> on stream #<STRING-OUTPUT-STREAM :CLOSED #x302000568CFD> . CLISP PRINT: Despite *PRINT-READABLY*, #<CONDITION #x0000000200233599> cannot be printed readably. ECL Cannot print object #<a CONDITION> readably. SBCL #<CONDITION {10047AF7D3}> cannot be printed readably.
Perfectly conforming.
The explanation of *print-readably* unwritten in the ANS is that it's purpose is to provide a guarantee that something written will later be rereadable without error, producing an object that is the "same" as the original. (The definition of "same" is a different Lisp execution complicated, but not the central focus here.) Mostly *print-readably* guarantees that the emitted sequence of character can be reread without signalling error.
All this depends on a bunch of other assumptions, and the necessary details are incomplete in the ANS. Here are just some of the issues:
What about *print-pretty* and dispatches defined in the current pprint-dispatch? If there is some such dispatch that would violate rereadability, should the printer ignore that *print-pretty* is true? A naive reading of the standard suggests this is a requirement (despite being operationally ridiculous) but perhaps the intention was that *print-readably* ought just bind *print-pretty* false. But if so, the ANS would have said so.
A few variables used by the pretty printer are specified to be set to NIL when printing readably, but otherwise, it's assumed the pretty printer will only change the new lines and indentation, not the actual graphic characters printed. Using a pretty printer dispatch table that would change what is printed has not been envisioned in the specification of *print-readably*.
What about other printer variables? One might define a print-object method that assumes that *print-base* will be the same at reread time -- but the ANS doesn't say this.
Definitely. Since we cannot assume a *read-base*, integers shall get the decimal dot when printing readably, or shall be printed with #o, #b #x or #r.
There are a great many more potential issues. We must conclude that *print-readably* is only a hook for user code to provide a _weak_ guarantee that no _other_ user code will do things that prevent printed output from being rereadable without likely notification at write time. But to achieve even this weak guarantee, user code must cooperate with what it knows about the printer and reader. That it applies to the printer representation of conditions might protect a mostly-numeric output file from containing an unreadable #<floating-point-underflow> object where a float is expected.
And since we cannot assume a *read-floating-point-format*, printing readably floating points should not use E or leave it implicit, but the specific floating point type markers, S, F, D and L. Only clisp prints numbers readably.
In any case, it cannot ensure either that the floating point types will have the required precision or more importantly, exponent ranges. Even clisp will gladly print long floats with more precision that what could be configured or could exist in the reading context.
Perhaps the ANS missed requiring that *print-readably* can assume the emitted sexpr will be reread inside a with-standard-io-syntax? But the ANS doesn't say this.
What about the package system? Whether the printed representation of a symbol has zero, one, or two colons depends on the current state of the package system. Should *print-readably* assume the package system will be the same at read time? Perhaps, but the ANS doesn't say this. And with-standard-io-syntax doesn't help with this.
clhs *print-readably* is rather clear:
If *print-readably* is true, some special rules for printing objects go into effect. Specifically, printing any object O1 produces a printed representation that, when seen by the Lisp reader while the standard readtable is in effect, will produce an object O2 that is similar to O1. The printed representation produced might or might not be the same as the printed representation produced when *print-readably* is false. If printing an object readably is not possible, an error of type print-not-readable is signaled rather than using a syntax (e.g., the ``#<'' syntax) that would not be readable by the same implementation. If the value of some other printer control variable is such that these requirements would be violated, the value of that other variable is ignored.
Specifically, if *print-readably* is true, printing proceeds as if *print-escape*, *print-array*, and *print-gensym* were also true, and as if *print-length*, *print-level*, and *print-lines* were false.
If *print-readably* is false, the normal rules for printing and the normal interpretations of other printer control variables are in effect.
Individual methods for print-object, including user-defined methods, are responsible for implementing these requirements.
If *read-eval* is false and *print-readably* is true, any such method that would output a reference to the ``#.'' reader macro will either output something else or will signal an error (as described above).
Notice that nothing is said about reading back the representation without error. This cannot be ensured, and therefore this is not specified.
Nothing is said of *package* or with-standard-io-syntax, only the standard READTABLE is specified for the context of reading back the representation output. Also, check the note about *read-eval*.
So you don't have to escape unconditionnally the symbols to print readably, but you have to qualify them with their packages. All implementations but clisp will just assume *package* won't change.
$ clall -r '(make-package "U")' '(let ((*print-readably* t)) (prin1-to-string (quote (a u::b :c 42 1.2s3 1.2f3 1.2d3 1.2l3))))'
Armed Bear Common Lisp --> "(A U::B :C 42 1200.0f0 1200.0f0 1200.0d0 1200.0d0)" Clozure Common Lisp --> "(A U::B :C 42 1200.0 1200.0 1200.0D0 1200.0D0)" CLISP --> "(|COM.INFORMATIMAGO.CLALL|::|A| |U|::|B| :|C| 42. 1200.0s0 1200.0f0 1200.0d0 1200.0L0)" ECL --> "(A U::B :C 42 1200.0 1200.0 1200.0d0 1200.0l0)" SBCL --> "(A U::B :C 42 1200.0 1200.0 1200.0d0 1200.0d0)"
$ clall -r '(make-package "U")' '(let ((*print-case* :downcase) (*print-readably* t)) (prin1-to-string (quote (a u::b :c 42 1.2s3 1.2f3 1.2d3 1.2l3))))'
Armed Bear Common Lisp --> #<PACKAGE U> Armed Bear Common Lisp --> "(|COM.INFORMATIMAGO.CLALL|::|A| |U|::|B| :|C| 42 1200.0f0 1200.0f0 1200.0d0 1200.0d0)" Clozure Common Lisp --> "(a u::b :c 42 1200.0 1200.0 1200.0D0 1200.0D0)" CLISP --> "(|COM.INFORMATIMAGO.CLALL|::|A| |U|::|B| :|C| 42. 1200.0s0 1200.0f0 1200.0d0 1200.0L0)" ECL --> "(a u::b :c 42 1200.0 1200.0 1200.0d0 1200.0l0)" SBCL --> "(a u::b :c 42 1200.0 1200.0 1200.0d0 1200.0d0)"
Also note how ABCL switches to qualifying the symbol when *print-case* is not :upcase anymore :-)
But printing atoms is usually performed by CL functions, called from the user PRINT-OBJECT methods. Your PRINT-OBJECT methods should just ensure that it prints readably user objects, and you can defer to CL the processing of the lisp types.