The problem generally appears if I have a class containing, for example, a couple of slots that would be filled with vectors. If I want to make the object of this class more-or-less transparent, I implement print-object
for it. And here I am faced with the problem:
- If I print everything in one line, REPL's heuristics are not good enough to determine how to arrange printable parts in multiple lines, causing everything to be shifted to the right (see example below).
- If I decide to split the output into multiple lines manually, I have a problem of how to indent everything properly, such that if this object is a part of another object, indentation is preserved (see example below for more clarity).
Here is the code. Consider two classes:
(defclass foo ()
((slot1 :initarg :slot1)
(slot2 :initarg :slot2)))
(defclass bar ()
((foo-slot :initarg :foo)))
And I have the following instances:
(defparameter *foo*
(make-instance 'foo
:slot1 '(a b c d e f g h i j k l m n o p q r s t u v)
:slot2 #(1 2 3 4 5 6 7 8)))
(defparameter *bar*
(make-instance 'bar
:foo *foo*))
What I want to see, is something like this:
> *bar*
#<BAR
foo-slot = #<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>>
Case 1: Printing everything in one line
Definitions of print-object
for these classes can be something like these:
(defmethod print-object ((obj foo) out)
(with-slots (slot1 slot2) obj
(print-unreadable-object (obj out :type t)
(format out "slot1 = ~A slot2 = ~A" slot1 slot2))))
(defmethod print-object ((obj bar) out)
(with-slots (foo-slot) obj
(print-unreadable-object (obj out :type t)
(format out "foo-slot = ~A" foo-slot))))
However, their printable representation is less than ideal:
> *foo*
#<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1 2 3 4 5
6 7 8)>
> *bar*
#<BAR foo-slot = #<FOO slot1 = (A B C D E F G H I J K L M N O P Q R S T U V) slot2 = #(1
2
3
4
5
6
7
8)>>
Case 2: Attempt to multiple line printing
Using multiple line printing, I don't know how to control indentation:
(defmethod print-object ((obj foo) out)
(with-slots (slot1 slot2) obj
(print-unreadable-object (obj out :type t)
(format out "~%~Tslot1 = ~A~%~Tslot2 = ~A" slot1 slot2))))
(defmethod print-object ((obj bar) out)
(with-slots (foo-slot) obj
(print-unreadable-object (obj out :type t)
(format out "~%~Tfoo-slot = ~A" foo-slot))))
Thus, *foo*
prints OK, but *bar*
isn't:
> *foo*
#<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>
*bar*
#<BAR
foo-slot = #<FOO
slot1 = (A B C D E F G H I J K L M N O P Q R S T U V)
slot2 = #(1 2 3 4 5 6 7 8)>>
In the past I tried to play around with print-indent
, but without a success (I couldn't see any effect from it, maybe wasn't using it correctly, SBCL 1.2.14).
Is there a (preferably simple) way to solve this?
~@<
at the beginning of the logical block and then no need to put arguments into list. And it is also possible to create a mandatory newline (respecting indentation) with~:@_
directive. – Teutonize