Reading/writing clojure

Revisiting my clojure frustrations, I was having problems getting things saved out to be recreated properly when loaded back in. This is something you take for granted perhaps, as a scheme programmer – but by restricting the set of containers I use, I’ve managed to get the right behavior from clojure.

user=> (def a (hash-map :number 43 :list () :string "hello"))
#'user/a
user=> (spit "test.txt" a)
nil
user=> (slurp "test.txt")
"{:string \"hello\", :list (), :number 43}"
user=> (read-string (slurp "test.txt"))
{:string "hello", :list (), :number 43}
user=> (:number (read-string (slurp "test.txt")))
43

As originally suggested by Sam – if I save a hash-map, I get one back when I parse the text – same for lists or normal data. This isn’t the case for struct-maps or records which I tried before. I can also do versioning with hash-maps, where one of the fields is the version number, and structures can change without breaking file compatibility. When you load an old version, you can automatically detect this and convert it on the fly by adding default values for new data, or removing old stuff.

This is essential for production work, especially with an online game – as you need to keep file formats and data structures flexible while doing the upgrades in an quick automated way to match the code which is running.

Here is a section of the Naked on Pluto code (in scheme, but the process will be similar in clojure for Germination X) it gives you an idea of the kind of changes we made throughout the development process:

(let* ((f (open-input-file filename))
         (l (read f)) ; read the file
         (v (car l)) ; first item is the version
         (g (cadr l))) ; second item is the graph
    ; the version allows us to preprocess the graph to fix
    ; things which have changed. do this here.
    (let ((g (if (< v 1) (add-vocab-to-nodes g) g)))
      (let ((g (if (< v 2) (add-message-types g) g)))
        (let ((g (if (< v 3) (add-bot-timers g) g)))
          (let ((g (if (< v 4) (add-old-messages-to-nodes g) g)))
            (let ((g (if (< v 5) (remove-dir-from-edges g) g)))
              (let ((g (if (< v 6) (add-hatface g) g)))
                (let ((g (if (< v 7) (add-votes g) g)))
                    ; g is now up to date, whatever the version was we loaded
                  ))))))))

Leave a Reply

Your email address will not be published. Required fields are marked *