Or see the list of project sponsors.
Documentation | 😀 |
Docstrings | 😀 |
Tests | 😀 |
Examples | 😀 |
RepositoryActivity | 😀 |
CI | 😀 |
This is a library by @scymtym. It provides a composable way to write print-object
methods for complex class hierarchies.
It has a good tutorial. Just to give you idea for cases where it can be useful, consider you have the following class hierarchy:
POFTHEDAY> (defclass user ()
((last-seen :initform (local-time:now)
:reader user-last-seen)))
POFTHEDAY> (defclass anonymous (user)
())
POFTHEDAY> (defclass registered-user (user)
((name :initarg :name :reader user-name)))
POFTHEDAY> (defclass admin (registered-user)
((privileges :initform nil
:initarg :privileges
:reader admin-privileges)))
For you want to define a print-object
methods for these objects which outputs all object field, then you'll have to repeat them all in each method, like this:
POFTHEDAY> (make-instance 'admin
:name "Bob"
:privileges '(:read-all :add-users))
#<ADMIN {10097677E3}>
POFTHEDAY> (make-instance 'registered-user
:name "Alla")
#<REGISTERED-USER {100458FE43}>
POFTHEDAY> (defmethod print-object ((user admin) stream)
(print-unreadable-object (user stream :type t :identity t)
(format stream "~A privileges=~A last-seen=~A"
(user-name user)
(admin-privileges user)
(user-last-seen user))))
POFTHEDAY> (defmethod print-object ((user registered-user) stream)
(print-unreadable-object (user stream :type t :identity t)
(format stream "~A last-seen=~A"
(user-name user)
(user-last-seen user))))
POFTHEDAY> (make-instance 'registered-user
:name "Alla")
#<REGISTERED-USER Alla last-seen=2020-07-29T16:18:57.595959+03:00 {100489DD43}>
POFTHEDAY> (make-instance 'admin
:name "Bob"
:privileges '(:read-all :add-users))
#<ADMIN Bob privileges=(READ-ALL ADD-USERS) last-seen=2020-07-29T16:17:15.458722+03:00 {10044E9183}>
This is cumbersome and not composable at all. Utilities.print-items
provides a protocol where each object can report about pieces it owns and a single print-object
method uses this information to output all necessary parts of the object presentation:
POFTHEDAY> (defmethod print-object ((user user) stream)
(print-unreadable-object (user stream :type t :identity t)
(print-items:format-print-items
stream
(print-items:print-items user))))
POFTHEDAY> (defmethod print-items:print-items append ((object user))
`((:last-seen ,(user-last-seen object)
"last-seen=~A")))
POFTHEDAY> (defmethod print-items:print-items append ((object registered-user))
`((:name ,(user-name object)
"~A "
((:before :last-seen)))))
POFTHEDAY> (defmethod print-items:print-items append ((object admin))
`((:privileges ,(admin-privileges object)
"privileges=~A "
((:after :name)
(:before :last-seen)))))
POFTHEDAY> (make-instance 'registered-user
:name "Alla")
#<REGISTERED-USER Alla last-seen=2020-07-29T16:45:50.711253+03:00 {100441F9C3}>
POFTHEDAY> (make-instance 'admin
:name "Bob"
:privileges '(:read-all :add-users))
#<ADMIN Bob privileges=(READ-ALL ADD-USERS) last-seen=2020-07-29T16:44:27.433569+03:00 {100440A133}>
There is also a special mixin class, which can be used instead of custom print-object
method:
POFTHEDAY> (remove-method #'print-object
(find-method #'print-object '()
'(user t)))
POFTHEDAY> (make-instance 'admin
:name "Bob"
:privileges '(:read-all :add-users))
#<ADMIN {1004537653}>
POFTHEDAY> (defclass user (print-items:print-items-mixin)
((last-seen :initform (local-time:now)
:reader user-last-seen)))
POFTHEDAY> (make-instance 'admin
:name "Bob"
:privileges '(:read-all :add-users))
#<ADMIN Bob privileges=(READ-ALL ADD-USERS) last-seen=2020-07-29T16:53:56.089722+03:00 {10073D2FF3}>
@scymtym did a great job, generalizing this printing facility. Use it when you have deep class hierarchies.