No they don't, not as a single entity. Debuggers let you see
individual locations, but don't create an enumerable/storable/copyable
environment.
> I want to capture the state of the Clojure runtime environment
> at a certain point in its execution and replicate it for a new
> thread, which is assigned to a new user so that I don't have
> to recreate that state for each user.
>
I know very well what you want to do. I've been trying to explain to
you that you can't, and why. Here again, I think you dropped the Java
analogy too soon. Try replacing 'Clojure' with 'Java' above. You can't
do that either. Java names and the storage locations to which they
refer are shared for all code loaded under the same classloader, in
all threads on which that code runs. Same for Clojure.
> > So a key presumption in this discussion is the need for
> > 'modifications' and I would ask - modifications to what, specifically
> > (i.e. the answer should not be 'the environment')? What is an example
> > of something a script would modify? Then we can talk about how to
> > isolate that.
>
> OK, take this script:
>
> (if ((somerandombooleanfn))
> (def a 88)
> (def a 99)
> )
>
That code shows you really haven't gotten the gist of Clojure yet.
> Now, in the same JVM, if user A executes this script and,
> simultaneously, user B executes this script and the fn returns 'true'
> for user A and false for user B, what is the value of the
> global symbol 'a'? Obviously, I'm having trouble understanding
> Clojure but I believe the following:
> 1) there is one symbol 'a' in the 'user' namespace after this
> code is executed.
One _var_ named a.
> 2) Neither of the threads bound the symbol 'a' locally while
> executing so
the _var_ user/a
> 3) Clojure has mediated the binding of the symbol
> and one of the threads 'won' (i.e it's binding was 'last in' and
> is now the current global binding of symbol 'a' (in the 'user'
> namespace)).
>
not symbol, var
> If (1), (2), and (3) above are true then the two users just had
> their programs interfere with each other. That's what I'm
> trying to avoid.
>
Then don't write code like that! The whole point of Clojure is that
programs that depend upon shared mutable state are to be avoided. If
you want script isolation then don't put your state in vars, or, make
sure your script entry point thread-locally binds the var with
'binding'.
Here's a more reasonable script:
(def a) ;a is unbound, will throw exception if accessed without a
binding
(defn script-entry-point [init]
(binding [a init]
... do rest of script, some of which might refer to a))
> > Scripts don't have/own vars. There are vars. They have namespace
> > segregated global names. Scripts should avoid modifying them if they
> > are not thread-locally bound. This is the same advice as for all
> > Clojure programs.
>
> OK, I see the distinction. But scripts create vars and give them root
> values? And they create symbols which point to those vars?
No, your understanding of vars/symbols is off.
Suppose you had this Java code:
public class User{
public static Object a;
...
}
and somewhere else (in a different class) you have this:
void foo()
{
if(User.a != null)
{
...
}
}
When foo is compiled, a symbolic reference to User.a is placed in the
bytecode. At some point prior to foo being accessed, that reference to
User.a is looked up via the classloader that loaded foo and resolved
to a particular location. When foo is called, the symbol/token "a" is
not used to find the location.
Most compilers work the same way - at some point during compilation/
linking/loading, names are resolved to locations and the names are not
used to find the locations during runtime, the resolved locations are
used directly.
Clojure works the same way. While you may see, and even access, the
namespaces/vars environment, that doesn't mean it's being used in the
way you presume. The _compiler_ does a one-time lookup of any vars
(essentially locations) used by a function and resolves all symbols
directly to those vars, and the static initializer of the class that
represents a function places those vars into fields that have been
compiled into the bytecode. Thus, when the function runs, symbols are
not used to look up the locations, the vars are used directly - >there
is no runtime lookup<.
This is the contrast I am trying to make with interpreters, where it
is quite common to pass a name->value environment through every
function call, and all symbolic references are resolved dynamically
via a runtime lookup in the environment. There it is easy to copy
environments and plug them into the call chain.
The tradeoffs are mostly in performance (pre-resolved access being
faster), but also, in Clojure's case, interoperability, since Clojure
code can be called from Java code that would have no knowledge of, or
access to, the environment. Putting environments in thread-local
storage complicates multithreaded programming.
I'm quite happy with the choices I've made for Clojure and the
resulting performance and ease of interop, so it's not going to change
in this area. You simply must consider it a compiled language like
Java.
> During
> the execution of the program they rebind symbols to point to different
> vars?
No, vars can be bound to different values. Every reference to user/a
under the same classloader is resolved to the same var object.
> So the set of symbols in all the namespaces is what I am calling
> the "environment" and a snapshot of that set at any point in the
> execution
> of the Clojure runtime environment is the "state" of the program.
>
...set of vars in all the namespaces...
Therein lies the rub - while you could create a snapshot of the
environment, you would have no mechanism of getting code to use it, as
the environment is not used by code at runtime. (Just like Java's
internal symbol table is not used for runtime access, only during
loading/linking).
Hope that helps,
Rich