11

I'm coding something like REPL Server. Request from users evaluates in such function:

(defn execute [request]
  (str (try
          (eval (read-string request))
        (catch Exception e (.getLocalizedMessage e)))))

Each client in separate thread. But they have the same namespace. How can I run code in dynamic created namespace ? So when new client connected, I want to create new namespace and to run client handling loop code there. Or maybe it's possible to run (eval ..) in other namespace ?

Thanks.

upd.
Solved!

Execute function:

(defn execute  
  "evaluates s-forms"  
  ([request] (execute request *ns*))  
  ([request user-ns]  
    (str  
      (try  
        (binding [*ns* user-ns] (eval (read-string request)))  
        (catch Exception e (.getLocalizedMessage e))))))

Each client gets it's own namespace by:

(defn generate-ns  
  "generates ns for client connection"  
  [] (let [user-ns (create-ns (symbol (str "client-" (Math/abs (.nextInt random)))))]  
    (execute (str "(clojure.core/refer 'clojure.core)") user-ns)  
    user-ns))`  

(defn delete-ns  
  "deletes ns after client disconnected"  
  [user-ns] (remove-ns (symbol (ns-name user-ns))))

offtop: How to make offsets in code snippets on begin of line ?

Jouni K. Seppänen
  • 43,139
  • 5
  • 71
  • 100
  • 1
    If your problem was solved by somebody's answer, please mark that answer as correct. If you came up with this answer yourself, please write it up as an answer in the answers section (not as part of the question) and mark it correct. Regarding formatting of code blocks, simply write them as separate paragraphs indented by four spaces (I edited your question to fix the formatting). Welcome to Stackoverflow! – Jouni K. Seppänen Oct 07 '11 at 14:32
  • The official policy is that you should post an answer to your question in this situation. However, some people don't like that practice, and downvote answers by OP. So the officially correct action is risky. – Mars Oct 14 '14 at 04:07

4 Answers4

18

Solved:

(binding [*ns* user-ns] (eval (read-string request)))
2

(symbol (str "client-" (Math/abs (.nextInt random)))

I just wanted to add, that this could be achieved with

(gensym "client-")

(I wanted to comment, but it turns our that I can't :))

BillyIII
  • 46
  • 2
1

Changing namespace means that you will have to reinitialize all the aliases, or refer to even clojure.core stuff with a fully qualified name:

user=> (defn alien-eval [ns str]
         (let [cur *ns*]
           (try ; needed to prevent failures in the eval code from skipping ns rollback
             (in-ns ns)   
             (eval (read-string str))
             (finally 
               (in-ns (ns-name cur))
               (remove-ns ns))))) ; cleanup, skip if you reuse the alien ns
#'user/alien-eval
user=> (alien-eval 'alien "(clojure.core/println clojure.core/*ns*)") ; note the FQN
#<Namespace alien> ; the effect of println
nil                ; the return value of alien-eval
skuro
  • 13,414
  • 1
  • 48
  • 67
0

You can write a macro that mimics

(defmacro my-eval [s] `~(read-string s))

It works better that eval because the symbol resolution of s occurs in the context that calls my-eval. Thanks to @Matthias Benkard for the clarifications.

viebel
  • 19,372
  • 10
  • 49
  • 83