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
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.)
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.
DMTCP might do the job in a clumsy way. Google on DMTCP: Distributed MultiThreaded CheckPointing. I use it to checkpoint interactive OCaml programs.