0

Using Clojure 1.8.0

I'm trying to get a defrecord with custom formatting in an otherwise default-formatted nested structure, for use with EDN, so I need a tagged representation. I could get away with the default one if it would come through in pprint, but I'd prefer a custom one. As it is, I can't get pprint to show the custom one without resorting to setting *print-pprint-dispatch* to pr, which destroys the nice line breaks, etc., that pprint provides.

user> (defrecord junkrecord [val])
user> (def junk1 (->junkrecord 10))
user> (def junk2 (->junkrecord 20))
user> (pprint {:key1 junk1, :key2 junk2, :key3 (java.time.LocalTime/now)})
{:key1 {:val 10},
 :key2 {:val 20},
 :key3 #object[java.time.LocalTime 0xbf97341 "15:04:43.487"]}

The defrecord shows up without the hashtag like the LocalTime object does, but the hashtag for the defrecord is what I want. This issue is mentioned as unresolved in https://dev.clojure.org/jira/browse/CLJ-1890 .

I created a print-method for my defrecord which works properly when used with (pr ...).

user> (defmethod print-method junkrecord [obj writer] (.write writer (str "#ThisIsWhatIwant" (.val obj))))
user> (pr junk1)
#ThisIsWhatIwant10

However when I run it through pprint, I lose the indentation, line breaks, etc.

user> (with-pprint-dispatch pr
    (pprint {:key1 junk1, :key2 junk2, :key3 (java.time.LocalTime/now)}))
{:key1 #ThisIsWhatIwant10, :key2 #ThisIsWhatIwant20, :key3 #object[java.time.LocalTime 0xa908e55 "15:10:09.634"]}

I was able to get it to work for a deftype as it behaves much more like a Java class in this regard, but deftypes are recommended for "low level" stuff, not domain stuff like a defrecord.

user> (deftype junktype [val])
user> (def junk3 (junktype. 30))
user> (pprint {:key1 junk3, :key2 (java.time.LocalTime/now)})
{:key1 #object[user.junktype 0x54c21b73 "user.junktype@54c21b73"],
 :key2 #object[java.time.LocalTime 0x20545fc3 "15:17:40.580"]}
user> (defmethod print-method junktype [obj writer] (.write writer (str "#ThisIsWhatIwant" (.val obj))))
user> (pprint {:key1 junk3, :key2 (java.time.LocalTime/now)})
{:key1 #ThisIsWhatIwant30,
 :key2 #object[java.time.LocalTime 0x499bdba8 "15:18:33.230"]}

I also played around with *print-dup* and (print-dup ...), etc. but this didn't yield anything.

So how do I get custom tagged printing for a defrecord while using pprint for the nice formatting? I've searched high and low, but have not found anything specific to this problem.

thanks!

Sonicsmooth
  • 2,673
  • 2
  • 22
  • 35
  • I changed to using a deftype, as this is probably closer to what I need anyway, since equality semantics, etc., are different for what I'm doing than just a named hash-map. – Sonicsmooth Mar 20 '18 at 04:15
  • I came across that post, but it didn't make too much sense at the time. I think I have a better grasp of things now, so I'll give it a try again. – Sonicsmooth Mar 22 '18 at 00:31

1 Answers1

0

The way I see it:

it seems it was proposed but did not make it to clojure 1.8 or 1.9

setup:

user=> (defrecord foo [val])
user.foo
user=> (def x (->foo 33))
#'user/x

now

Either) drop pprint and use str or some such

user=> (str {:a-foo x})
"{:a-foo #user.foo{:val 33}}"

Or) provide a custom dispatch for your type

user=> (defmethod clojure.pprint/simple-dispatch user.foo [f] (pr f))
#object[clojure.lang.MultiFn 0x401f3c4 "clojure.lang.MultiFn@401f3c4"]

user=> (pprint {:a-foo x :a-goo x :b x :c x :d x :f x})
{:a-foo #user.foo{:val 33},
 :a-goo #user.foo{:val 33},
 :b #user.foo{:val 33},
 :c #user.foo{:val 33},
 :d #user.foo{:val 33},
 :f #user.foo{:val 33}}

for completeness sake - reading:

user=> (clojure.edn/read-string 
         {:readers {'user.foo map->foo}} 
         "{:a-foo #user.foo{:val 33}}")
{:a-foo #user.foo{:val 33}}
birdspider
  • 3,034
  • 1
  • 16
  • 25