how to print-dup records?

190 views
Skip to first unread message

Seth

unread,
Jan 31, 2011, 12:26:57 PM1/31/11
to Clojure
This is my attempt:

(defn record? [a]
(and (map? a) (not (instance? clojure.lang.APersistentMap a))))

(defmethod print-dup clojure.lang.IPersistentMap [m, ^Writer w]
(if (record? m)
(do (.write w "#=(")
(print-dup (class m) w) (.write w ". ")
(doall (map #(do (print-dup % w) (.write w " ")) (map second (seq
m))))
(.write w ")")
)
(do
(#'clojure.core/print-meta m w)
(.write w "#=(")
(.write w (.getName (class m)))
(.write w "/create ")
(#'clojure.core/print-map m print-dup w)
(.write w ")"))))


;;used from http://groups.google.com/group/clojure/browse_thread/thread/cb5246d07142a3dc?fwc=2&pli=1
(defn frm-save
"Save a clojure form to file."
[file form]
(with-open [w (java.io.FileWriter.
(if (instance? File file) file (File. file)))]
(binding [*out* w *print-dup* true] (prn form))))

(defn frm-load
"Load a clojure form from file."
[file]
(with-open [r (java.io.PushbackReader.
(java.io.FileReader. (if (instance? File file) file (File.
file))))]
(let [rec (read r)]
rec)))



However, it appears that the reader cant read classes
For example

(defrecord Foo [a])
(frm-save "/home/seth/Desktop/test" (Foo. 2))
(frm-load "/home/seth/Desktop/test")

Another problem is that it saves it as the ns it is in, for example my-
ns.Foo, and when you reload it it wont find my-ns.Foo unless you have
required it. But, editing the test file to just output

#=(#=foo. 2 3 )

and then loading that throws a class not found exception.

Any general way to print-dup these records?

Alex Miller

unread,
Jan 31, 2011, 7:33:27 PM1/31/11
to Clojure
We wrapped defrecord in a macro that created a factory function then
had print-dup print a call to that function.

https://github.com/david-mcneil/defrecord2

You can also find pprint in there too.


On Jan 31, 11:26 am, Seth <wbu...@gmail.com> wrote:
> This is my attempt:
>
> (defn record? [a]
>   (and (map? a) (not (instance? clojure.lang.APersistentMap a))))
>
> (defmethod print-dup clojure.lang.IPersistentMap [m, ^Writer w]
>   (if (record? m)
>     (do (.write w "#=(")
>         (print-dup (class m) w) (.write w ". ")
>         (doall (map #(do (print-dup % w) (.write w " ")) (map second (seq
> m))))
>         (.write w ")")
>         )
>     (do
>       (#'clojure.core/print-meta m w)
>       (.write w "#=(")
>       (.write w (.getName (class m)))
>       (.write w "/create ")
>       (#'clojure.core/print-map m print-dup w)
>       (.write w ")"))))
>
> ;;used fromhttp://groups.google.com/group/clojure/browse_thread/thread/cb5246d07...

Seth

unread,
Feb 1, 2011, 9:59:43 PM2/1/11
to Clojure
Looks like it will do!
One change, though, that I made for myself is that
(defrecord2 (name constructor-name) [args] Protocol1 ...)
so you can do protocols
or (defrecord2 name [args] Protocol1...) for default

Seth

unread,
Feb 2, 2011, 10:18:02 AM2/2/11
to Clojure
Ive just thought up that using print-dup for records might become a
nightmare. This is because it will now matter which ns you load it
from. If it loads an ns where the constructor function isnt defined or
isnt imported, it will throw an error. This will become a nightmare
when mixes records from various nses. A possible solution would be to
create a generic function, called new-record, which takes the ns and
the constructor function (find-ns ns-of-function function-symbol) and
then call that. Pretty simple.

Meikel Brandmeyer

unread,
Feb 2, 2011, 10:30:40 AM2/2/11
to Clojure
Hi,

why don't you just fully qualify the constructor function? Works from
everywhere.

Sincerely
Meikel

Seth

unread,
Feb 4, 2011, 12:12:31 PM2/4/11
to Clojure
cool! thought you had to refer or something the namespace, but i guess
the reader works differently.

changes are here
https://github.com/Storkle/defrecord2

basically, i modified it to work with print-dup and i got rid of the
pprint methods and changed the way constructor names are specified. so
now

(defrecord2 (hi my-constructor) [a b] Protocol1 ....)
and (my-constructor {:a 2 :b 3})

(pprint a-record) ;pprints like a normal record

but
(binding [*print-dup* true] (print-str (my-constructor {:a 2})))
will output something like this

#=(my.namespace/my-constructor .....)

Reply all
Reply to author
Forward
0 new messages