Fork me on GitHub
#sci
<
2023-06-05
>
Macroz17:06:53

Can someone confirm this easily? I inject logging functions into my SCI context. Then I evaluate code in that context that defines a function which uses the logging functions. I store the resulting context where the function is defined for later use. Now my question is, if I fork the context, and re-define the logging functions (with more context available at this time), does my function refer to the old logging functions or the new ones? From my experiments it seems like the function defined in the forked context still refers to the old logging functions, and the new ones are not used.

borkdude17:06:30

I think it's easier to understand what you are talking about if you make a small repro in working code

Macroz04:06:24

Ok, something like this

(defn info1 [& args] (apply prn "info1" args))
(defn info2 [& args] (apply prn "info2" args))

(def sci-ctx1
  (sci/init {:namespaces {'log {'info info1}}}))

(def sci-ctx2
  (let [ctx (-> sci-ctx1 sci/fork)]
    (sci/eval-string* ctx "(defn hello [x] (log/info \"hello\" x))")
    ctx))

(def sci-ctx3
  (-> sci-ctx2
      sci/fork
      (sci/merge-opts {:namespaces {'log {'info info2}}})))

(sci/eval-string* sci-ctx3 "(hello \"world\")")
; => "info1" "hello" "world"

Macroz04:06:27

My initial expectation was that it would print "info2" because that binding is meant to override the previous binding of log/info in the context but the hello function maintains reference to the old one.

borkdude09:06:31

How it works is this: You provide log/info as raw function, not as a SCI var When hello is analyzed it references this function directly, so no matter what changes are made later on, it will always hold on to the same function. Maybe a solution to your problem would be:

(ns scratch
  (:require [sci.core :as sci]))

(def ^:dynamic *log-fn* prn)
(defn info1 [& args] (apply *log-fn* "info1" args))

(def sci-ctx1
  (sci/init {:namespaces {'log {'info info1}}}))

(def sci-ctx2
  (let [ctx (-> sci-ctx1 sci/fork)]
    (sci/eval-string* ctx "(defn hello [x] (log/info \"hello\" x))")
    ctx))

(binding [*log-fn* identity]
  (sci/eval-string* sci-ctx2 "(hello \"world\")"))

borkdude09:06:05

you can also create a SCI dynamic var, but I think the above would be similar

Macroz09:06:11

Yeah, I've implemented a solution pretty much like this.

👍 2
Macroz09:06:09

So context bindings do not automatically become vars, or they are resolved at analyze/compile time is what I thought.

Macroz09:06:25

Thanks for confirming the behavior and also confirming the solution 👍

borkdude09:06:36

yes, and even if you provide a var (with sci/copy-var) it will resolve against that particular var, so even if you insert a new var into a new context, it will not see that one, since it has a reference to the old var

2