pretty-printing a record using a custom method in

2019-01-26 11:00发布

问题:

In Clojure 1.5.0, how can I provide a custom pretty-printer for my own record type, defined with defrecord.

(defrecord MyRecord [a b])

(defmethod print-method MyRecord [x ^java.io.Writer writer]
  (print-method (:a x) writer))

(defmethod print-dup MyRecord [x ^java.io.Writer writer]
  (print-dup (:a x) writer))

(println (MyRecord. 'a 'b)) ;; a -- OK
(clojure.pprint/pprint (MyRecord. 'a 'b)) ;; {:a a, :b b} -- not OK, I want a

I would like clojure.pprint/pprint to also use my cutsom printer (which now, should just pretty-prints whatever is in the field a of the record for illustration purposes).

回答1:

clojure.pprint namespace uses different dispatch mechanism then the clojure.core print functions. You need to use with-pprint-dispatch to customize the pprint.

(clojure.pprint/with-pprint-dispatch print  ;;Make the dispatch to your print function
  (clojure.pprint/pprint (MyRecord. 'a 'b)))

To customize the simple dispatcher, add something like:

(. clojure.pprint/simple-dispatch addMethod MyRecord pprint-myrecord)


回答2:

This has been very confusing, with correct answers to different parts of the problem spread around various places, so I put it all together in the hopes that someone might save some time.

I went through all the permutations of defining the multimethods print-method, print-dup, and simple-dispatch for the defrecord, and setting dynamic vars *print-pprint-dispatch* and *print-dup*. I put an example defrecord through both pr and pprint and came up with the following flowchart. It makes sense now I can see everything at once.

The permutation code is brute force; I just looked at the outputs and created the Visio diagram directly. Nothing fancy.

(ns print-test
  (:require [clojure.pprint :as pp]))


(defrecord tr000 [val])
(defrecord tr001 [val])
(defrecord tr010 [val])
(defrecord tr011 [val])
(defrecord tr100 [val])
(defrecord tr101 [val])
(defrecord tr110 [val])
(defrecord tr111 [val])


;;(defmethod print-method       tr000 [obj writer]  (.write writer "tr000 print-method"))
;;(defmethod print-dup          tr000 [obj writer]  (.write writer "tr000 print-dup"))
;;(defmethod pp/simple-dispatch tr000 [obj]         (.write *out* "tr000 simple-dispatch"))

;;(defmethod print-method       tr001 [obj writer]  (.write writer "tr001 print-method"))
;;(defmethod print-dup          tr001 [obj writer]  (.write writer "tr001 print-dup"))
(defmethod pp/simple-dispatch tr001 [obj]         (.write *out* "tr001 simple-dispatch"))

;;(defmethod print-method       tr010 [obj writer]  (.write writer "tr010 print-method"))
(defmethod print-dup          tr010 [obj writer]  (.write writer "tr010 print-dup"))
;;(defmethod pp/simple-dispatch tr010 [obj]         (.write *out* "tr010 simple-dispatch"))

;;(defmethod print-method       tr011 [obj writer]  (.write writer "tr011 print-method"))
(defmethod print-dup          tr011 [obj writer]  (.write writer "tr011 print-dup"))
(defmethod pp/simple-dispatch tr011 [obj]         (.write *out* "tr011 simple-dispatch"))

(defmethod print-method       tr100 [obj writer]  (.write writer "tr100 print-method"))
;;(defmethod print-dup          tr100 [obj writer]  (.write writer "tr100 print-dup"))
;;(defmethod pp/simple-dispatch tr100 [obj]         (.write *out* "tr100 simple-dispatch"))

(defmethod print-method       tr101 [obj writer]  (.write writer "tr101 print-method"))
;;(defmethod print-dup          tr101 [obj writer]  (.write writer "tr101 print-dup"))
(defmethod pp/simple-dispatch tr101 [obj]         (.write *out* "tr101 simple-dispatch"))

(defmethod print-method       tr110 [obj writer]  (.write writer "tr110 print-method"))
(defmethod print-dup          tr110 [obj writer]  (.write writer "tr110 print-dup"))
;;(defmethod pp/simple-dispatch tr110 [obj]         (.write *out* "tr110 simple-dispatch"))

(defmethod print-method       tr111 [obj writer]  (.write writer "tr111 print-method"))
(defmethod print-dup          tr111 [obj writer]  (.write writer "tr111 print-dup"))
(defmethod pp/simple-dispatch tr111 [obj]         (.write *out* "tr111 simple-dispatch"))


(def t000 (->tr000 10))
(def t001 (->tr001 20))
(def t010 (->tr010 30))
(def t011 (->tr011 40))
(def t100 (->tr100 50))
(def t101 (->tr101 60))
(def t110 (->tr110 70))
(def t111 (->tr111 80))
(def recs [t000 t001 t010 t011 t100 t101 t110 t111])


(def dt (java.time.LocalTime/now))
(defmethod print-dup java.time.LocalTime [obj writer] (.write writer "datetime_dup"))

(println "(pr ...) outputs the following")
(doseq [rec recs]
  (doseq [prppd [nil #(.write *out* "pprint-dispatch with" %)]]
    (binding [pp/*print-pprint-dispatch*
              (if prppd #(.write *out* (str "pprint-dipatch-with" %))
                  pp/*print-pprint-dispatch*)]
      (doseq [pd [false true]]
        (binding [*print-dup* pd]
          (let [ppdstr (format "%6s" (boolean pp/*print-pprint-dispatch*))
                dupstr (format "%6s" *print-dup*)
                outstr (pr-str rec)]
            (binding [*print-dup* false]
              (println ppdstr dupstr ":" outstr))))))))

(println "\n(pprint ...) outputs the following")
(doseq [rec recs]
  (doseq [prppd [false true]]
    (binding [pp/*print-pprint-dispatch*
              (if prppd #(.write *out* (str "pprint-dipatch-with" %))
                  pp/*print-pprint-dispatch*)]
      (doseq [pd [false true]]
        (binding [*print-dup* pd]
          (let [outstr
                (with-out-str
                  (pp/pprint {:ppdstr prppd
                              :dupstr pd
                              :dt dt
                              :strobj rec}))]
            (binding [*print-dup* false]
              (println outstr "\n"))))))))


回答3:

Maybe not ideal, but I haven't found better than pr and pr-str.

Example REPL session :

  (ns my-ns)

  (defprotocol Foo
    (bazfn [this]))

  (defrecord Bar [a]
    Foo
    (bazfn [this] 123))


  (pr-str (Bar. "ok")) ;;=> "#my_ns.Bar{:a \"ok\"}"
  (pr (Bar. "ok"))     ;; prints the same as above