0

I asked a related question here: Clojure: How do I turn clojure code into a string that is evaluatable? It mostly works but lists are translated to raw parens, which fails

The answer was great but I realized that is not exactly what I needed. I simplified the example for stackoverflow, but I am not just writing out datum, I am trying to write out function definitions and other things which contain structures that contain lists. So here is a simple example (co-opted from the last question).

I want to generate a file which contains the function:

(defn aaa []
  (fff :update {:bbb "bbb" :xxx [1 2 3] :yyy (3 5 7)}))

Everything after the :update is a structure I have access to when writing the file, so I call str on it and it emerges in that state. This is fine, but the list, when I load-file on this generated function, tries to call 3 as a function (as it is the first element in the list).

So I want a file which contains my function definition that I can then call load-file and call the functions defined in it. How can I write out this function with associated data so that I can load it back in without clojure thinking what used to be lists are now function calls?

2
  • So do I have to traverse the list of forms and replace all persistent lists with quoted persistent lists? If so, how do I do that? Commented Feb 14, 2012 at 0:29
  • Can you just quote the map, or does it need to be evaluated too? (defn aaa [] (fff :update '{:bbb "bbb" :xxx [1 2 3] :yyy (3 5 7)}))? If that doesn't work you'll need to quote all seqs (if (seq? x)). Commented Feb 14, 2012 at 2:03

3 Answers 3

1

You need to quote the structure prior to obtaining the string representation:

(list 'quote foo)

where foo is the structure.

Three additional remarks:

  1. traversing the code to quote all lists / seqs would not do at all, since the top-level (defn ...) form would also get quoted;

  2. lists are not the only potentially problematic type -- symbols are another one (+ vs. #<core$_PLUS_ clojure.core$_PLUS_@451ef443>);

  3. rather than using (str foo) (even with foo already quoted), you'll probably want to print out the quoted foo -- or rather the entire code block with the quoted foo inside -- using pr / prn.

The last point warrants a short discussion. pr explicitly promises to produce a readable representation if *print-readably* is true, whereas str only produces such a representation for Clojure's compound data structures "by accident" (of the implementation) and still only if *print-readably* is true:

(str ["asdf"])
; => "[\"asdf\"]"

(binding [*print-readably* false]
  (str ["asdf"]))
; => "[asdf]"

The above behaviour is due to clojure.lang.RT/printString's (that's the method Clojure's data structures ultimately delegate their toString needs to) use of clojure.lang.RT/print, which in turn chooses output format depending on the value of *print-readably*.

Even with *print-readably* bound to true, str may produce output inappropriate for clojure.lang.Reader's consumption: e.g. (str "asdf") is just "asdf", while the readable representation is "\"asdf\"". Use (with-out-str (pr foo)) to obtain a string object containing the representation of foo, guaranteed readable if *print-readably* is true.

Sign up to request clarification or add additional context in comments.

1 Comment

Marczyk, you are a lifesaver. Seriously. Also, I see now this all warrants some research into the subtleties of read/eval/print, as this is kind of at the heart of the matter! Thanks for the glimpse into this wooly world.
0

Try this instead...

(defn aaa []
  (fff :update {:bbb "bbb" :xxx [1 2 3] :yyy (list 3 5 7)}))

Comments

0

Wrap it in a call to quote to read it without evaluating it.

1 Comment

The deal is I want to eval it, everything works fine but lists!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.