sci

jyn 2025-08-15T15:36:55.635229Z

i am running into weird differences in SCI name resolution depending on whether a bound host function is involved or not. here is a simple example that reproduces the problem:

(sci/eval-string "(ns xxx) (def y :y) (ns user) (identity (do (use 'xxx) y))")
that throws an exception that says Could not resolve symbol: y. but if i remove the identity call, it works fine:
user=> (sci/eval-string "(ns xxx) (def y :y) (ns user) (do (use 'xxx) y)")
:y
why the difference here? is there simple way to avoid this?

✅ 1
jyn 2025-08-15T15:37:34.994139Z

note that identity is clojure.core/identity, i.e. it is bound from the host

jyn 2025-08-15T15:39:00.045199Z

oh hm this is not related to host functions at all; this reproduces the issue:

user=> (sci/eval-string "(ns xxx) (def y :y) (ns user) (defn my-identity [x] x) (my-identity (do (use 'xxx) y))")

jyn 2025-08-15T15:40:13.003039Z

as a workaround i can wrap the do in eval but that causes me trouble for other complicated reasons and i would rather not have to

jyn 2025-08-15T15:41:16.126609Z

oh huh this doesn't seem related to SCI at all. i can reproduce this in the host

borkdude 2025-08-15T15:43:12.344809Z

> oh huh this doesn't seem related to SCI at all. i can reproduce this in the host 👍 AKA https://technomancy.us/143

👀 1
jyn 2025-08-15T15:47:02.218809Z

that's very helpful, thanks 🙂 i think i can contort my code so it always has a top-level do, let me try that now

borkdude 2025-08-15T15:51:42.003019Z

note that macros evaluating to do do work:

$ clj -M -e "(ns xxx) (def y :y) (ns user) (defmacro foo [x] x)  (foo (do (use 'xxx) y))"
#'xxx/y
#'user/foo
:y

👀 1
jyn 2025-08-15T15:52:31.998839Z

and i remember that sci has a way to bind host macros, yeah ... i'll keep that in mind, thanks 🙂

jyn 2025-08-15T16:20:27.891389Z

i keep having this pattern show up in my codebase:

; NOTE: parse-string only parses a single form, so we have to wrap the file in `do`
(let [lisp (str "(do " f ")")
      form (sci/parse-string cx lisp)]
  ) ; ...
this is fine and it works, but it feels kinda bad? is there an intended way to do this?

borkdude 2025-08-15T18:45:34.353819Z

You can create a parsing loop like this:

(let [rdr (sci/reader f)]
  (loop []
    (let [form (sci/parse-next ctx rdr)]
       (when (not= ::sci/eof form)
         (sci/eval-form form)
         (recur))))
something like that