ANN: A pretty printer for Clojure

81 views
Skip to first unread message

Tom Faulhaber

unread,
Mar 12, 2009, 3:05:14 AM3/12/09
to Clojure
I have now "released" the first version of my pretty printer as part
of my cl-format library. It is released under the EPL.

The pretty printer has two functions that you probably care about:

(pprint obj) will pretty print the given object, and
(pp) at the REPL will pretty print the last result output, i.e. the
value in *1.

The pretty printer currently supports two modes: simple and code.
Simple mode prints structure in a standard way that's good for data.
Code mode understands lots of Clojure forms (defn, binding vectors,
condp, etc.) and attempts to print them in an idiomatic way.

Cl-format is on github at http://github.com/tomfaulhaber/cl-format.
There is a Readme there with instructions, examples, limitations and
futures. I won't even try to put examples here, because google groups
wreaks havoc on formatting.

The simplest way to get some pretty printing happiness:
1) Download the jar: http://github.com/tomfaulhaber/cl-format/raw/master/release/cl-format.jar
2) Put it in your classpath.
3) Fire up your REPL
4) (use 'com.infolace.format)
5) Use pprint and pp as described above.

This is definitely a first release and there are sure to be bugs. And
I know there are things missing. So let me know if you're having
problems and I'll try to get things fixed up ASAP.

Enjoy!

Tom

Rich Hickey

unread,
Mar 12, 2009, 4:56:03 AM3/12/09
to clo...@googlegroups.com

This looks very useful Tom. Would you consider contributing it to
Clojure?

Rich


Jeffrey Straszheim

unread,
Mar 12, 2009, 9:49:14 AM3/12/09
to clo...@googlegroups.com
Awesome!  I expect I'll be trying it out tonight.

Oh, and I hope this goes into contrib -- it'll keep my classpath shorter.

David Nolen

unread,
Mar 12, 2009, 10:41:59 AM3/12/09
to clo...@googlegroups.com
Amazing stuff. In particular this finally makes debugging macros sane.

For those of you that are using swank-clojure you only need to make minor modifications to swank-clojure to pretty-print your macro expansions to the macro expansion buffer.  Even better you can move the cursor to subform macros and expand those inline as well.

For example here is an example from Paul Graham's continuations chapter from On Lisp that I expanded, I did not do this by hand promise ;)

(do
 (clojure.core/defmacro dft2 [tree]
(clojure.core/seq
(clojure.core/concat
 (clojure.core/list '=dft2)
 (clojure.core/list '*cont*)
 (clojure.core/list tree))))
 (clojure.core/defn =dft2 [*cont* tree]
   (reset! *saved* nil)
   (clojure.core/let [*cont* (clojure.core/fn
      [node]
      (cond
(= node 'done) (*cont* nil)
:else (do (print node) (restart))))]
     (=dft-node *cont* tree))))

For you swank-clojure users, all you need to do to get this feature right now is to include the cl-format library in basic.clj (swank-clojure/swank/commands/basic.clj) and change apply-macroexpander to look like the following:

(defn- apply-macro-expander [expander string]
  (let [astr (with-out-str 
(with-pprint-dispatch *code-dispatch*
  (pprint (expander (read-from-string string)))))]
    (subs astr 0 (dec (count astr)))))

Of course once cl-format makes into clojure-contrib this should just be the default behavior (or setq-able option) for swank-clojure ;)

David

Konrad Hinsen

unread,
Mar 12, 2009, 12:10:05 PM3/12/09
to clo...@googlegroups.com
On Mar 12, 2009, at 8:05, Tom Faulhaber wrote:

> I have now "released" the first version of my pretty printer as part
> of my cl-format library. It is released under the EPL.

From what I have seen in my first tests, this is likely to become an
essential part of my Clojure environment. Thanks!

Konrad.

Tom Faulhaber

unread,
Mar 12, 2009, 12:28:00 PM3/12/09
to Clojure
Rich,

I would be happy to make it a contribution (it's the least I can do!).
I've had a CA sitting on my desk unread and unsigned for about 3
weeks. It is now read, signed, and in an envelope. I'll send it off
this morning.

Everyone,

Thanks for the kind words. I'm glad you like it.

David's use case in slime/swank was one of the motivators for me
writing this. Thanks for showing us how to do the integration, David!
I hope we see a lot of other use cases like that.

Upcoming is the ability to create custom dispatch tables which will
open the door to an even broader set of use cases.



On Mar 12, 1:56 am, Rich Hickey <richhic...@gmail.com> wrote:
> On Mar 12, 2009, at 3:05 AM, Tom Faulhaber wrote:
>
>
>
>
>
> > I have now "released" the first version of my pretty printer as part
> > of my cl-format library. It is released under the EPL.
>
> > The pretty printer has two functions that you probably care about:
>
> > (pprint obj) will pretty print the given object, and
> > (pp) at the REPL will pretty print the last result output, i.e. the
> > value in *1.
>
> > The pretty printer currently supports two modes: simple and code.
> > Simple mode prints structure in a standard way that's good for data.
> > Code mode understands lots of Clojure forms (defn, binding vectors,
> > condp, etc.) and attempts to print them in an idiomatic way.
>
> > Cl-format is on github athttp://github.com/tomfaulhaber/cl-format.
> > There is a Readme there with instructions, examples, limitations and
> > futures. I won't even try to put examples here, because google groups
> > wreaks havoc on formatting.
>
> > The simplest way to get some pretty printing happiness:
> > 1) Download the jar:http://github.com/tomfaulhaber/cl-format/raw/master/release/cl-format...

Mark Volkmann

unread,
Mar 12, 2009, 12:50:54 PM3/12/09
to clo...@googlegroups.com
On Thu, Mar 12, 2009 at 2:05 AM, Tom Faulhaber <tomfau...@gmail.com> wrote:
>
> I have now "released" the first version of my pretty printer as part
> of my cl-format library. It is released under the EPL.
>
> The pretty printer has two functions that you probably care about:
>
> (pprint obj) will pretty print the given object, and
> (pp) at the REPL will pretty print the last result output, i.e. the
> value in *1.

Are you planning to include scripts at some point that support pretty
printing a source file? For example, I'd like to do this from a
terminal window:

$ cljpp foo.clj > foo2.clj

--
R. Mark Volkmann
Object Computing, Inc.

Tom Faulhaber

unread,
Mar 12, 2009, 12:53:05 PM3/12/09
to Clojure
Expanding on David's earlier example of pretty printing, we can set
the dispatch to *code-dispatch* and bind *print-suppress-namespaces*
to true and get the following (apologies for google messing up my
formatting):

(do
(defmacro dft2 [tree]
(seq (concat (list '=dft2) (list '*cont*) (list tree))))
(defn =dft2 [*cont* tree]
(reset! *saved* nil)
(let [*cont* (fn
[node]
(cond
(= node 'done) (*cont* nil)
:else (do (print node) (restart))))]
(=dft-node *cont* tree))))

Which is not always what you want, since it loses the namespace info
that backquote adds, but for me it's usually what I want because it
looks like the code I would have written.

A couple of notes for the detail-oriented: You can see the special
code formats for defmacro, defn, binding vectors, and cond in
operation here. You can also see that fn needs special treatment and
doesn't have it yet. (But it will, real soon now.)

Tom Faulhaber

unread,
Mar 12, 2009, 1:06:59 PM3/12/09
to Clojure
> Are you planning to include scripts at some point that support pretty
> printing a source file? For example, I'd like to do this from a
> terminal window:
>
> $ cljpp foo.clj > foo2.clj

Mark,

Yeah, I've thought about that and the simple version is very straight-
forward and I'm planning to do that soon (for testing as much as
anything).

But there are a couple of issues that make this less useful than you'd
like. The reader loses at least two significant pieces of information
that you really want to retain when you're reformatting code files:
comments and metadata defined with #^{} on defs and the like.

My thought is that the right approach to this would be a modified
version of the Clojure reader that's lossless and a modified dispatch
that would handle the comments correctly. But I haven't spent any time
looking at the reader code to see how hard that would be.

Tom

David Nolen

unread,
Mar 12, 2009, 1:47:50 PM3/12/09
to clo...@googlegroups.com
So that people can copy and paste the change in basic.clj

(defn- apply-macro-expander [expander string]
  (binding [*print-suppress-namespaces* true]
    (let [astr (with-out-str 
(with-pprint-dispatch *code-dispatch*
  (pprint (expander (read-from-string string)))))]
      (subs astr 0 (dec (count astr))))))

Fantastic.

David Nolen

unread,
Mar 12, 2009, 2:07:39 PM3/12/09
to clo...@googlegroups.com
I suppose the following is more idiomatic:

(defn- apply-macro-expander [expander string]
  (let [astr (with-out-str 
      (binding [*print-suppress-namespaces* true]
(with-pprint-dispatch *code-dispatch*
  (pprint (expander (read-from-string string))))))]
      (subs astr 0 (dec (count astr)))))

Tom Faulhaber

unread,
Mar 12, 2009, 3:30:19 PM3/12/09
to Clojure
Write can produce pretty output directly to a string and without the
trailing newline, making this a little shorter:

(defn- apply-macro-expander [expander string]
(binding [*print-suppress-namespaces* true]
(with-pprint-dispatch *code-dispatch*
(write (expander (read-from-string string)) :pretty true :stream
nil))))

Completely untested! :-)

Tom

On Mar 12, 11:07 am, David Nolen <dnolen.li...@gmail.com> wrote:
> I suppose the following is more idiomatic:
>
> (defn- apply-macro-expander [expander string]
>   (let [astr (with-out-str
>        (binding [*print-suppress-namespaces* true]
>  (with-pprint-dispatch *code-dispatch*
>    (pprint (expander (read-from-string string))))))]
>       (subs astr 0 (dec (count astr)))))
>

David Nolen

unread,
Mar 12, 2009, 3:46:21 PM3/12/09
to clo...@googlegroups.com
Works great.

budu

unread,
Mar 14, 2009, 12:16:56 PM3/14/09
to Clojure
I just tried it, this is fantastic! We'll finally be able to debug
macros while keeping our sanity. Many thanks for this and I hope it
will be added directly into Clojure.

On Mar 12, 3:05 am, Tom Faulhaber <tomfaulha...@gmail.com> wrote:
> I have now "released" the first version of my pretty printer as part
> of my cl-format library. It is released under the EPL.
>
> The pretty printer has two functions that you probably care about:
>
> (pprint obj) will pretty print the given object, and
> (pp) at the REPL will pretty print the last result output, i.e. the
> value in *1.
>
> The pretty printer currently supports two modes: simple and code.
> Simple mode prints structure in a standard way that's good for data.
> Code mode understands lots of Clojure forms (defn, binding vectors,
> condp, etc.) and attempts to print them in an idiomatic way.
>
> Cl-format is on github athttp://github.com/tomfaulhaber/cl-format.
> There is a Readme there with instructions, examples, limitations and
> futures. I won't even try to put examples here, because google groups
> wreaks havoc on formatting.
>
> The simplest way to get some pretty printing happiness:
> 1) Download the jar:http://github.com/tomfaulhaber/cl-format/raw/master/release/cl-format...
Reply all
Reply to author
Forward
0 new messages