views:

150

answers:

2

Clojure has a large number functions/macros for working with namespaces and java package imports. To my (limited) understanding the set up of namespaces can be considered state in a clojure process (repl).

When working iteratively at a REPL session, especially when source files are (re)-loaded, I can find it easy to get confused - often when I make a mistake or syntax error in namespace configuration. Other times I want to try out refactoring namespaces/aliases/reference filters but can't easily back out of existing namespace state without restarting the REPL.

For example I would like to be able to checkpoint namespace configuration - such as after the main body of code is loaded at the repl - then get back to that "clean-slate" after trying out some library imported at the REPL so that I can immediately test a source file that imports a filtered subset of methods in that library as part of the ns macro.

Can people recommend ways to save and restore namespace configuration?

+6  A: 

I'm sure there's something wrong with this, as I just wrote it in answer to this question, but I see myself using this in my projects, for sure. Just :import it (have it in its own file in your project) and use it liberally.

(ns world)


(defn save-world
  []
  (let [syms (filter identity (distinct (for [i (ns-map *ns*)] (first i))))]
    (for [i syms]
      (vector i
              (ns-resolve *ns* i)))))

(defn destroy-world-but
  [saved]
  (let [syms (filter identity (distinct (for [i (ns-map *ns*)] (first i))))]
    (for [i syms]
      (if-not (or (= (ns-resolve *ns* i) (ns-resolve *ns* saved))
                  (= (ns-resolve *ns* i) (ns-resolve *ns* 'restore-world))
                  (= (ns-resolve *ns* i) (ns-resolve *ns* '*ns*)))
        (ns-unmap *ns* i)))))

(defn restore-world
  [saved]
  (clojure.core/map
   #(intern *ns* (clojure.core/first %) (clojure.core/second %))
   saved))

First, save a the state of your world (the one you want to go back to) like this:

(def *save* (save-world))

Then do whatever you want–experiment. When you're ready to go back to your former state:

(destroy-world-but '*save*)
(restore-world *save*)

And you should be good to go!

(Hope this works! Was working for me–please let me know if there's a problem. I'm sure there's a better way to do this, too, but this works and it's how far I got tonight. I'm sure I'll revise.)

Isaac Hodes
+3  A: 

This won't always work. You can remove Vars from a namespace with ns-unmap, but other pieces of code may still hold references to those definitions.

Clojure, because it is based on the JVM, has no concept of a "memory image" like some Common Lisp or Scheme implementations.

Stuart Sierra
Warning well taken. So I could get in a lot of trouble if I were trying to save and restore the state of a complex long running app for example. But for the simple use case of functions required/aliased at a single repl session I suspect I am probably ok doing something like what Issac shows below.
Alex Stoddard