Skip to content

Instantly share code, notes, and snippets.

@wobh
Last active September 20, 2019 22:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wobh/7b7d205ba80cdeb707b5c97096f856c7 to your computer and use it in GitHub Desktop.
Save wobh/7b7d205ba80cdeb707b5c97096f856c7 to your computer and use it in GitHub Desktop.
Figured out `print-object :around`, per https://twitter.com/wobher/status/1144821786485432320
(defclass being ()
((health :accessor health :initarg :health)
(injury :accessor injury :initarg :injury)))
(defmethod make-load-form ((being being) &optional environment)
(declare (ignore environment))
`(make-instance ',(class-name (class-of being))
:health ,(health being)
:injury ,(injury being)))
(defmethod print-object ((being being) stream)
(if *print-readably*
(multiple-value-bind (make-form setup-form)
(make-load-form being)
(format stream "~W~@[~&~W~]" make-form setup-form))
(if *print-escape*
(print-unreadable-object (being stream :type t :identity t)
(format stream
(if *print-pretty*
"~<~@{~S ~S~^ ~@_~}~:>"
"~{~S ~S~^ ~}")
(cddr (make-load-form being))))
(format stream
"~(~A~)"
(class-name (class-of being))))))
(defclass hunter (being)
((quiver :accessor quiver :initarg :quiver))
(:default-initargs
:quiver 5
:health 2
:injury 0))
(defmethod make-load-form :around ((being hunter) &optional environment)
(declare (ignore environment))
(append (call-next-method)
(list :quiver (quiver being))))
(defmethod print-object :around ((being hunter) stream)
(cond ((null *print-escape*)
(format stream "A bold ")
(call-next-method)
(format stream ", keen of ear and eye"))
(t
(call-next-method))))
(defclass wumpus (being)
((psyche :accessor psyche :initarg :psyche))
(:default-initargs
:psyche :asleep
:health 3
:injury 0))
(defmethod make-load-form :around ((being wumpus) &optional environment)
(declare (ignore environment))
(append (call-next-method)
(list :psyche (psyche being))))
(defmethod print-object :around ((being wumpus) stream)
(cond ((null *print-escape*)
(format stream
"A ~A "
(ecase (psyche being)
(:asleep "sleeping")
(:enraged "raging")))
(call-next-method))
(t
(call-next-method))))
;;;; Tests
(let* ((object (make-instance 'wumpus :health 3 :injury 1 :psyche :enraged))
(subject (eval (read-from-string (write-to-string object :readably t)))))
(check-type subject wumpus)
(assert (= 3 (health subject)))
(assert (= 1 (injury subject)))
(assert (eql :enraged (psyche subject))))
(let* ((object (make-instance 'hunter :quiver 1 :health 2 :injury 1))
(subject (write-to-string object :escape t :pretty nil)))
(assert (= 2 (search "HUNTER" subject)))
(assert (search ":QUIVER 1" subject))
(assert (search ":HEALTH 2" subject))
(assert (search ":INJURY 1" subject)))
(let* ((object (make-instance 'wumpus))
(subject (write-to-string object :escape nil)))
(assert (string= "A sleeping wumpus"
subject)))
(let* ((object (make-instance 'hunter))
(subject (write-to-string object :escape nil)))
(assert (string= "A bold hunter, keen of ear and eye"
subject)))
@wobh
Copy link
Author

wobh commented Sep 19, 2019

A few ideas I've been trying to explore in this gist, most of which have puzzled me for quite a while:

  • how to use :around methods effectively, especially for providing specialized contents for print-unreadable-object.
  • how to write print-object methods that make appropriate uses of the Common Lisp print system, especially the print variables.
  • how to do "naive" serialization of objects.

On that last one, I had already thought about writing properties methods for extracting the key parameters for make-instance, stuffing them into print-unreadable-objects and writing a parser for that. I thought I would have to use a MOP library just to get at them. But then I stumbled on make-load-form, and I realized, for the price of a little extra object infrastructure, I could do it with a standard method. I cobbled up the demo model, and progressively refactored it. Thanks to everyone who suggested something.

TODO: I could include some tests to specify my assumptions.
TODO: A more thorough search for examples of solutions to these questions.
TODO: *print-pretty* format string could probably be prettier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment