How to override .toString for a struct?

141 views
Skip to first unread message

Jung Ko

unread,
Sep 21, 2009, 7:23:05 PM9/21/09
to clo...@googlegroups.com
Hi,

Can someone teach me how I can override the "toString" method for a struct ?

Here's a code snippet that shows what I'm trying to do:

user> (defstruct bookinfo :book :filename)
user> (struct bookinfo "hello" "world") => {:book "hello", :filename "world"}

I would like to override the "toString" method on a struct so that the
following behavior is true:

user> (struct bookinfo "hello" "world") => "hello"

In Common Lisp, I would simply define a method on (print-object). What
would be the equivalent thing in Clojure?

I already tried the following but it doesn't seem to work
(sorry...i've been programming Clojure for only two days):

user> (defmulti .toString class)
user> (defmethod .toString clojure.lang.PersistentStructMap [s]
(:book s))

Thanks for your help.

Regards,
Jung Ko

Mike Hinchey

unread,
Sep 21, 2009, 10:49:06 PM9/21/09
to clo...@googlegroups.com
You can (defmethod print-method), but you'll need a :type in the meta of your struct.

-Mike

Jarkko Oranen

unread,
Sep 22, 2009, 4:31:05 AM9/22/09
to Clojure


On Sep 22, 2:23 am, Jung Ko <koj...@gmail.com> wrote:
> Hi,
>
> Can someone teach me how I can override the "toString" method for a struct ?
>
> Here's a code snippet that shows what I'm trying to do:
>
> user> (defstruct bookinfo :book :filename)
> user> (struct bookinfo "hello" "world") => {:book "hello", :filename "world"}
>
> I would like to override the "toString" method on a struct so that the
> following behavior is true:
>
> user> (struct bookinfo "hello" "world") => "hello"
>
> In Common Lisp, I would simply define a method on (print-object). What
> would be the equivalent thing in Clojure?

As Mike said, it's done by adding a method to print-method, but you
will need metadata... "structs" in clojure are nothing but an
optimisation of maps... That is, in all situations structmaps are (at
least, should be) interchangeable with a regular persistent map. They
do not create their own type or anything, so you can't override their
toString method directly.

However, since print-method uses :type metadata, you can do the
following:

;; using ::foo to get namespace-qualified keywords is good for type
tags, because it avoids collisions
;; typed-book could be a struct-map, or in fact any implementation of
Map that also supports metadata
(def typed-book (with-meta {:book "somebook"} {:type ::my-book})) ;
the latter map is the metadata
(defmethod print-method ::my-book [thebook writer]
(print-method (:book thebook) writer)) ; falls back to the string
writer

(print typed-book) ; prints 'somebook'

Jung Ko

unread,
Sep 22, 2009, 2:50:30 PM9/22/09
to clo...@googlegroups.com
On Tue, Sep 22, 2009 at 1:31 AM, Jarkko Oranen <chou...@gmail.com> wrote:

> As Mike said, it's done by adding a method to print-method, but you
> will need metadata... "structs" in clojure are nothing but an
> optimisation of maps... That is, in all situations structmaps are (at
> least, should be) interchangeable with a regular persistent map. They
> do not create their own type or anything, so you can't override their
> toString method directly.
>
> However, since print-method uses :type metadata, you can do the
> following:
>
> ;; using ::foo to get namespace-qualified keywords is good for type
> tags, because it avoids collisions
> ;; typed-book could be a struct-map, or in fact any implementation of
> Map that also supports metadata
> (def typed-book (with-meta {:book "somebook"} {:type ::my-book})) ;
> the latter map is the metadata
> (defmethod print-method ::my-book [thebook writer]
> (print-method (:book thebook) writer)) ; falls back to the string
> writer
>
> (print typed-book) ; prints 'somebook'

Thank you Mike and Jarkko for the detailed answer. It works as you described. :)

Sincerely,
Jung Ko

Reply all
Reply to author
Forward
0 new messages