No context found in: sci.ctx-store/*ctx* with (map find-ns ['user]))
Closed this issue · 3 comments
Version
0.10.47
Platform
Linux, JVM
Problem
No context found in: sci.ctx-store/ctx with (map find-ns ['user])).
Here find-ns fails in lazy evaluations context of map, works fine with the strict mapv
repro
(defn- eval-sexp [sexp]
(eval-string (str sexp)))
(eval-sexp '(map find-ns ['user])) ; => No context found in: sci.ctx-store/*ctx*
(eval-sexp '(mapv find-ns ['user])) ; => [#object[sci.lang.Namespace 0x54f40d66 "user"]]
;; Even without `find-ns` we still do need strict `mapv` here:
(eval-sexp
'(let [ns-objects (all-ns)
dirs (mapv clojure.repl/dir-fn ns-objects)]
(map count dirs))) ; => (0 563 12 3 2 10 21 10 2)expected behavior
map and mapv return the same sequence elements
See the use case
https://github.com/alexei-matveev/genko/blob/master/src/genko/sci.clj#L29
And thank you for the great product!
Hey @alexei-matveev!
The evaluated expression returns a lazy sequence. When this is evaluated SCI normally uses the context it creates or is passed explicitly (in the case of eval-string*). But since the expression is lazy, the context goes out of scope.
You can read more about it here:
https://github.com/babashka/sci/blob/master/CHANGELOG.md#01046-2025-06-18
and here:
https://github.com/babashka/sci?tab=readme-ov-file#laziness
One solution could be to make an explicit context and set it as the global context with sci/reset-ctx.
Thanks! The second link, https://github.com/babashka/sci?tab=readme-ov-file#laziness, was eye opening. Forcing the lazy seq in the SCI context did the trick.
In fact the advice from the error message to use sci.ctx-store/reset-ctx! is kinda
red herring, after the fact. The issue was indeed that we continue evaluating
lazy seq after leaving SCI context:
;; Number of bindings pro namespace. NOTE: `doall` *inside* SCI is
;; important for lazy sequence such as the `for`-expression here!
;; once you leave the dynamic context of the SCI you cannot count on
;; daynamic `*ctx*` having a correct value!
;;
;; (eval-sexp '(for [ns ['user]] (find-ns ns))) ; WRONG!
;; => Exception with "No context found in: sci.ctx-store/*ctx* ..."
(eval-sexp
'(doall
(for [ns (all-ns)]
[(str ns) (count (clojure.repl/dir-fn ns))])))
=>
(["user" 0]
["clojure.core" 563]
["clojure.set" 12]
["Math" 3] ; 0 <> 3 without `doall`
["clojure.edn" 2]
["clojure.repl" 10]
["clojure.string" 21]
["clojure.walk" 10]
["clojure.template" 2])
;; Alternatively use `mapv` instead of lazy seq:
(eval-sexp
'(let [ns-objects (all-ns)
dirs (mapv clojure.repl/dir-fn ns-objects)]
(map count dirs))) => (0 563 12 3 2 10 21 10 2)Glad to have answered your question