Discussion:
PRINT-OBJECT for built-in classes
Edi Weitz
2015-08-29 09:10:15 UTC
Permalink
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.

? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >

My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).

Thanks,
Edi.

[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
Daniel Kochmański
2015-08-29 09:50:47 UTC
Permalink
If I understand correctly 22.2.1.4 states, that current
*print-pprint-dispatch* tables takes precedence over "user-defined
print-object methods because the current pprint dispatch table is
consulted first" – that's if *print-pretty* is T.

If *print-pretty* is nil, then description of PRINT-OBJECT (22.4.11)
states:

"Users may write methods for print-object for their own classes if they
do not wish to inherit an implementation-dependent method."

what might be a hint, that users can't write methods for print-object
for classes which are not "their own".

Regards,
Daniel
Post by Edi Weitz
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.
? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >
My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).
Thanks,
Edi.
[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
--
Daniel Kochmański | Poznań, Poland
;; aka jackdaniel

"Be the change that you wish to see in the world." - Mahatma Gandhi
Thomas Burdick
2015-08-29 10:04:13 UTC
Permalink
Hi Edi,
That certainly reads to me as though the intent is for print-object to be the decision point (at least in cases where *print-pretty* is false).
However, as far as I understand 11.1.2.1.2:
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:[...]
19. Defining a method for a standardized generic function which is applicable when all of the arguments are direct instances of standardized classes.
I think the much-feared "consequences are undefined" phrasing there means the implementation is free to act as though the method you defined is in fact defined, but completely ignore it when doing dispatch. (or reformat your hdd or make monkeys fly or whatever).
-Thomas
Date: Sat, 29 Aug 2015 11:10:15 +0200
Subject: PRINT-OBJECT for built-in classes
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.
? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >
My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).
Thanks,
Edi.
[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
Edi Weitz
2015-08-29 14:20:13 UTC
Permalink
Thomas,

That was the piece of the puzzle I had missed.

Thanks,
Edi.
Post by Thomas Burdick
Hi Edi,
That certainly reads to me as though the intent is for print-object to be
the decision point (at least in cases where *print-pretty* is false).
Except where explicitly allowed, the consequences are undefined if any of
the following actions are performed on an external symbol of the COMMON-LISP
[...]
19. Defining a method for a standardized generic function which is
applicable when all of the arguments are direct instances of standardized
classes.
I think the much-feared "consequences are undefined" phrasing there means
the implementation is free to act as though the method you defined is in
fact defined, but completely ignore it when doing dispatch. (or reformat
your hdd or make monkeys fly or whatever).
-Thomas
Date: Sat, 29 Aug 2015 11:10:15 +0200
Subject: PRINT-OBJECT for built-in classes
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.
? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >
My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).
Thanks,
Edi.
[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
Steve Haflich
2015-08-29 15:16:22 UTC
Permalink
A couple points:

First, I disagree with this detail in Thomas' analysis, and can't quite
understand what it claims.

the implementation is free to act as though the method you defined is in
fact defined, but completely ignore it when doing dispatch.

Unclear to what this "doing dispatch". print-object is presumably a
standard-generic-function, and it is called in normal printing, and how
CLOS handles that call is well defined. If user code has defined a
print-object method for a user-code-defined standard-class or
structure-class, print-object _must_ invoke that method in the usual way.
And if that method calls call-next-method, it could well be the case that
some implementation-provided method receives control, possibly even a
method specialized on class t.

There can be no bypassing of normal CLOS operation for print-object.

What the prohibition on defining methods on system classes means is that
user code cannot affect how non-pretty printing handles classes defined in
the ANS, which is a larger set than built-in classes. User code also
cannot (defmethod print-object ((x standard-object) stm) ...) even though
standard-object is not a built-in class.

Unfortunately, the prohibition in the ANS is not quite watertight. The
prohibition is oddly based on symbols that name classes and which are
exported from the CL package. One could imagine an additional built-in
implementation class interposed into the class lattice (perhaps a
sub/supertype of character or vector) named by a non-exported symbol. The
ANS prohibition technically allows user code to (re)define a print-object
method on that symbol, although this clearly violates the spirit and
intention of the prohibition.

It makes sense to prohibit changes to how built-in classes are printed,
sine in rare cases operation of other modules may depend on printed
representation. Suppose #'car works by making a TCP connection to a
relational database server that stores the car and cdr of each cons. To
make that connection the implementation must be able to depend on the
standard printing functions behaving normally. Changes to the several
printer special vars can be masked using with-standard-io-syntax, and the
current pprint-dispatch table can be lambda bound, but there is no simple
or efficient way to shadow print-object methods.

Finally, here is what everyone has been missing about this matter.

A conforming implementation must include CLOS, and must non-pretty write
using print-object. But a conforming implementation could include just a
single print-object method:

(defmethod print-object (x stm) (write x :stream stm)) ; write presumably
contains a big typecase

X3J13 considered CLOS to be important. But existing CLtL1 implementations
already had working printers that handled all CLtL1 objects and which
obviously did not use CLOS method dispatch. Only minor changes would be
necessary to accommodate printing of standard-objects, but to gain
acceptance new features were often couched in definitions that imposed the
least-possible impact on existing implementations. That's why the
requirements for print-object of implementation classes is so loose.
Post by Edi Weitz
Thomas,
That was the piece of the puzzle I had missed.
Thanks,
Edi.
Post by Thomas Burdick
Hi Edi,
That certainly reads to me as though the intent is for print-object to be
the decision point (at least in cases where *print-pretty* is false).
Except where explicitly allowed, the consequences are undefined if any of
the following actions are performed on an external symbol of the
COMMON-LISP
Post by Thomas Burdick
[...]
19. Defining a method for a standardized generic function which is
applicable when all of the arguments are direct instances of standardized
classes.
I think the much-feared "consequences are undefined" phrasing there means
the implementation is free to act as though the method you defined is in
fact defined, but completely ignore it when doing dispatch. (or reformat
your hdd or make monkeys fly or whatever).
-Thomas
Date: Sat, 29 Aug 2015 11:10:15 +0200
Subject: PRINT-OBJECT for built-in classes
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.
? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >
My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).
Thanks,
Edi.
[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
Pascal Costanza
2015-08-29 11:23:16 UTC
Permalink
The consequences are undefined if you do this. See http://www.lispworks.com/documentation/HyperSpec/Body/11_abab.htm item 19.

Pascal
Post by Edi Weitz
The following "works" in three different CL implementations I tried
while in three others the way complex numbers are printed doesn't
change.
? (defmethod print-object ((obj complex) stream)
(format stream "#< ~A + ~A * I >"
(realpart obj) (imagpart obj)))
#<STANDARD-METHOD PRINT-OBJECT (COMPLEX T)>
? #c(1 1)
#< 1 + 1 * I >
My understanding of 22.1.2 of the standard is that each Lisp MUST have
a PRINT-OBJECT method for complex numbers. The question then is
whether I'm allowed to redefine it like above (I think I am) and/or
whether an implementation is allowed to accept this redefinition
without a warning but then to ignore it (which, as I said, is what
happens in three respectable Lisps).
Thanks,
Edi.
[Note: There's no point in talking me out of this as I don't actually
want to do it anyway. It's just an example and I'm only interested in
what exactly is governed by the standard. Complex numbers are also
just an example. I'm interested in PRINT-OBJECT for built-in classes
in general.]
--
Pascal Costanza
The views expressed in this email are my own, and not those of my employer.
Loading...