I started working on a pretty-printer as well. I wouldn't show it to
anybody yet, but I also don't want any unnecessary duplicated effort.
So I'll go ahead and show what I've got so far, and then we can decide
how to proceed. I have no interest in pursuing my solution if it's not
a good approach, or if anyone else would rather pursue it.
That is, I want to use a pretty printer, not necessarily write one. :-)
> user> (def something '(a b c d (e f g h i) j k (l m n) (o p q r s t u
> v) w x y (z)))
> #'user/something
> user> (pp something 20)
I don't have nice API yet, so for now I have to use binding:
user=> (binding [*max-width* 20] (pprint something))
(a
b
c
d
(e f g h i)
j
k
(l m n)
(o p q r s t u v)
w
x
y
(z))
> user> (def things {:one "another" :two {:map "inside" :a "map"} :three
> [1 2 3 4 5] :four "still making things up" :five :done})
> #'user/things
> user> (pp things)
user=> (pprint things)
{:five
:done
:three
[1 2 3 4 5]
:two
{:a "map", :map "inside"}
:four
"still making things up"
:one
"another"}
I still need to add commas and collapse key/vals onto a single line
when possible.
> user> (pp (range 1000))
> [Thrown class java.lang.StackOverflowError]
user=> (pprint (range 5000))
(0
1
2
3
4
[...manually snipped here...]
4996
4997
4998
4999)
Not a problem, though I don't have *line-limit* yet.
I allow \newlines to print, which helps with long multi-line strings:
(defn
gen-and-load-class
"Generates and immediately loads the bytecode for the specified
class. Note that a class generated this way can be loaded only once
- the JVM supports only one class with a given name per
classloader. Subsequent to generation you can import it into any
desired namespaces just like any other class. See gen-class for a
description of the options."
[name & options]
(let
[{:keys [name bytecode]} (apply gen-class (str name) options)]
(.. clojure.lang.RT ROOT_CLASSLOADER (defineClass (str name) bytecode))))
I'd like to add a *detect-code* option that looks for "well-known"
symbols that are used in code (defn, let, etc.) and can format the
required args differently than the "rest" args. This would allow some
code forms to look more natural.
Anyway, it's definitely a work in progress. What I've got so far is
attached. All thoughts and comments are welcome.
--Chouser
Am 18.11.2008 um 17:29 schrieb stev...@acm.org:
> What could be some good strategies to adapt the code I have here to
> Clojure, where tail calls are not eliminated and structs are not lazy?
There is the lazy-map package[1], which also allows lazy (struct)maps.
However it is not updated to 1094+, yet.
I only skimmed through your code and I don't know the paper, but
maybe I can give some general tips.
As always: don't copy code blindly! Take a step back and look from a
distance, how you can *translate* the code. For example, in the
show-list-children function, the recursion is just used for iteration.
It starts with x, do something to (first x) and then calls itself
with (rest x). So the first step is to translate this into a loop
recur pair. The next step is to see, that one can also write this
as (doseq [child x] (do-something-to x)), or in case its the result
you are interested in and not the side-effects: (map #(do-something-to
%) x).
So don't just copy the code, but understand what it does and then
ask: "how would I do this in Clojure?"
Stuart's "PCL goes Clojure" series[2] is great example for this.
Just my 2¢.
Sincerely
Meikel
[1]: http://kotka.de/projects/clojure/lazy-map.html
[2]: http://blog.thinkrelevance.com/2008/9/16/pcl-clojure
That's certainly what it does, but I don't think it has to. My plan
was to use *print-length* and *print-level* to cause pr-str to bail
out if it's getting too long. I can't decide if this general approach
is an ugly hack or an elegant re-use of existing code.
> But then, your implementation actually works and doesn't run out of
> stack space on short lists. :-)
I wanted to post what I had right away, but I will now take the time
to understand your code, so I can come up with own opinion about how
we ought to proceed.
--Chouser
One option: You could use a seq instead of all the various structs.
This would allow you to remove all the doc-foo helper functions.
Instead of calling those helpers, in most cases you could simply build
a vector [:NIL] instead of (doc-nil), [:TEXT " "] instead of (doc-text
" "). That way, when you need to recurse, you could use a lazy
expression like (lazy-cat [:concat] [(flatten (:doc1 x))] [(flatten
(:doc2 x))])
This would of course require you to change everywhere that currently
expects a struct with a :type to instead use (first x) for the type
and the rest of the seq as args.
Is your current version visible anywhere? Perhaps a more holistic
look would reveal some other options.
--Chouser