So I apologize - I didn't mean to privately message you before about how to manage namespaces when coding a repl using sci - you very kindly replied with the following code:
(def !last-ns (volatile! @sci/ns))
(defn eval-string [s]
(sci/binding [sci/ns @!last-ns]
(let [rdr (sci/reader s)]
(loop [res nil]
(let [form (sci/parse-next @!ctx rdr)]
(if (= :sci.core/eof form)
(do
(vreset! !last-ns @sci/ns)
res)
(recur (sci/eval-form @!ctx form))))))))I'm not using that code quite yet, but if I try something like (ns client) in my repl, I get the following error, and I'm curious what's causing it:
Can't change/establish root binding of #'clojure.core/*ns* with set
Maybe if I used the code you showed, that somehow wouldn't happen but if that's the case, I'm confused.
@pmooser The problem is that (ns client) will change the value of the dynamic var *ns* but it may only be changed inside a binding. eval-string and eval-string* do that automatically, but eval-form does not
With "do that automatically" I mean: they surround the evalution with a binding
So is it sufficient for me just to bind (using sci/binding ) ... sci/ns ?
which also means that the value of *ns* is restored after the binding
yes
Or do I need to use "normal" (cljs) binding of *ns* ?
ok
Ok great - that does indeed fix it. Now I'll integrate the rest of what you gave me. Thanks - I have things almost perfectly working I think ...
@borkdude In cljs, what's the practical difference between using a volatile vs. storing a value in an atom ?
(in this context, I mean)
@pmooser volatile is a light weight atom with less features and therefore a bit more performant
Sure, but given that performance in a repl (between entered data) is sort of gated by typing speed, it seems like that difference isn't materially important here. In any case, it sounds like it isn't required, but I appreciate the additional info!
"less features" means not thread safe and should be used only in isolation.
yes, but we're talking CLJS here so threads are not a concern
oops, sorry ) too many channels )
With "here" I mean in the context of @pmooser’s question, not this channel > In cljs, what's the practical difference
Given a sci context, is there a way to say "what is the sci namespace for symbol x " ?
(outside of sci itself ... I'm trying to put bindings together for the very first call to the sci evaluator ... once I've been through there, it's not really a problem)
I suppose I could eval something and get sci to return the namespace, but it seems like there must be a better way.
@pmooser There currently isn't a better way than eval-ing something :)
But I guess we could expose (sci/find-ns ctx sym)?
I've needed that myself too. I could do that right now
I suppose I shouldn't be afraid of eval-ing something - I just have written my own interface (since my use of sci is an adaptation of something I used to have) and I have a symbol in hand which corresponds to the namespace I want, but the actual place the namespace gets created is "somewhere else" (where I'm creating namespaces and copying existing cljs namespaces), so what I have easy access to is simply the ctx ...
I also don't know enough about the sci representation of namespaces to do anything other than call "eval" safely, since I don't know exactly how they evolve over time.
indeed, the representation inside the context is subject to change, but the outer API should remain stable
but is find-ns something you were looking for?
(find-ns ... ...) -> SCI ns object?Yes, that would work perfectly in my case!
good, just a minute then
Do you think it's sufficiently "safe" (from an API stability point of view) to do something like:
(keys (:namespaces @(:env sci-ctx)))
to find the list of namespace (symbols) that sci knows about?
(or is there a better way to enumerate namespaces)
This isn't a vital feature, but I was curious.
@pmooser There is a better way, which is to evaluate (all-ns) - I could also expose that one, just a moment
It's really nice to be able to enumerate namespaces on the cljs side.
@pmooser Something like this?
user=> (require '[sci.core :as sci])
nil
user=> (def ctx (sci/init {}))
#'user/ctx
user=> (def all-nss (sci/all-ns ctx))
#'user/all-nss
user=> (map sci/ns-name all-nss)
(user clojure.core clojure.set clojure.edn clojure.repl clojure.string clojure.walk clojure.template)Yup, I think that would work great for my purposes.
fcf7e6067f091fd2cf2843660349e820c4449dd8Thank you ... trying some of this out (it may take me a little bit)
Ok, I imagine I'm doing something wrong.
So I create my namespaces and I have my context. Then I pull out a specific namespace (based on a config file), which in this case is user:
(sci/find-ns sci-ctx (:default-ns repl-config))
That returns the right ns, which has the right name.
However, when I call sci to evaluate something, I set up bindings like this:
(sci/binding [sci/ns (:current-ns @state)] ...)
And when I call eval, after I do the eval, if I ask sci/ns for its name, I get *ns*Let me see what it says inside binding but before eval
So that happens even before I call eval.
Please be more precise, make a code example
Yes, one moment.
(let [ns (sci/find-ns (:sci-env @state) 'user)]
;; prints "user"
(println "1: name ns" (sci/ns-name ns))
(sci/binding [sci/ns ns]
;; prints "*ns*"
(println "2: name ns" (sci/ns-name sci/ns))))The result of (sci/ns-name ns) prints *ns* ?
The result of sci/ns-name (as shown in the code snippet you requested) is "user".
I don't get to which part "`;; prints "ns""` refers to
(sci/ns-name ns) --> "user"
(sci/ns-name sci/ns) --> *ns*
the following println
Each comment above the println that says "prints" says what the print does
yes, this is expected. sci/ns is a SCI var. you should first deref it
oof
Yes, I see you do that in your code example you kindly provided. It just went over my head.
Sorry and thank you.
No problemo
Make sense?
@borkdude Ok, I can confirm both changes are working for me (all-ns and find-ns).