1
votes

I'm unclear why in the following code snippet, foo is being defined in the "user" namespace, and not the one I've bound to *ns* in the binding closure. Can someone explain what I'm missing?

$ clj
Clojure 1.4.0

user=> (let [nspace (create-ns (gensym "sandbox"))]
            (binding [*ns* nspace]
                     (print (ns-name *ns*))
                     (def foo 6)))
sandbox3#'user/foo

user=> foo
6

user=> (in-ns 'sandbox3)
#

sandbox3=> foo
CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:0)

sandbox3=> (def bar 7)
#'sandbox3/bar

sandbox3=> bar
7

sandbox3/user=> (in-ns 'user)
#

user=> foo
6

user=> bar
CompilerException java.lang.RuntimeException: Unable to resolve symbol: bar in this context, compiling:(NO_SOURCE_PATH:0) 
2

2 Answers

3
votes

You'll need to use eval:

   (let [nspace (create-ns (gensym "sandbox"))]
            (binding [*ns* nspace]
                     (print (ns-name *ns*))
                     (eval '(def foo 6))))

eval pays attention to *ns*, but most of Clojure does not.

EDIT: Yes, I just suggested that a person use eval :p

For context, I've been corresponding with op and he doesn't want a real sandbox as he trusts the code going into his system, he simply needed a way to evaluate inputted code inside of different namespaces!

So, if you're a newbie with this question and you're not sethwm, please refer to Marczyk's answer. This solution is very specific to the OP's use case and not a generally good way to do much of anything.

6
votes

The namespace in which def will create a Var is determined at compile time. The binding is not in effect then (binding introduces runtime bindings).

def should only be used at top level or inside something like a top-level let form. The further one drifts away from a simple top-level def, the greater the care that one should exercise. For example binding is certainly similar to a top-level let in many ways and the value part of a def expression will be evaluated with the expected thread-local bindings in place:

user=> (let [nspace (create-ns (gensym "foo"))]
         (binding [*ns* nspace]
           (def foo *ns*)))
#'user/foo
user=> foo
#<Namespace foo3>

The fashion in which the Var is created in the first place is, however, completely unaffected by the runtime state when execution reaches the def form.

To create Vars dynamically, use intern -- it's a regular function with completely predictable behaviour.