sci

jyn 2025-07-20T18:50:01.927199Z

hi! i am trying to write a wrapper function around sci/macro and having trouble. this is my code:

(defn copy-macro [sym] `(do ^:sci/macro (fn [_&form# _&env# & rest
#] (~sym rest#))))
(sci/init {:bindings {'html (copy-macro h/html)})
that gives an error i don't understand:
Syntax error macroexpanding clojure.core/fn at (blossom/parse.clj:22:32).
(blossom/_&form blossom/_&env & clojure.core/rest) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
blossom/_&form - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list

borkdude 2025-07-20T19:05:55.344659Z

is this in clojure or clojurescript btw? in JVM clojure you can copy macros with sci/copy-var

jyn 2025-07-20T21:08:27.682849Z

clojure, but i want to compile it to a native executable with GraalVM and i'm not sure if copy-var works there

borkdude 2025-07-20T21:10:20.991859Z

yes, it works also for that

borkdude 2025-07-20T21:10:36.387229Z

as long as you use copy-var on the top level, not in a function

borkdude 2025-07-20T21:10:56.024349Z

everything in SCI is designed with graal native-image in mind

jyn 2025-07-20T21:11:15.939509Z

oh very cool

jyn 2025-07-20T21:11:23.946099Z

i will probably do that then 🙂 thank you!

jyn 2025-07-20T18:53:10.008449Z

oh wait apparently load-file was doing caching hm

jyn 2025-07-20T18:53:26.664209Z

... oh it works now

jyn 2025-07-20T18:53:27.407139Z

huh

jyn 2025-07-20T21:09:23.706789Z

is there a way to know which namespaces are loaded by a (require) function call? my use case is that i loaded hiccup2.core into the SCI interpreter, but it keeps failing because it wants things like hiccup.util that aren't loaded

borkdude 2025-07-20T21:11:29.787899Z

can you explain how you added hiccup2.core to SCI?

jyn 2025-07-20T21:11:55.508139Z

like so:

(defn copy-ns [ns]
    (let [binding (sci/create-ns ns)
          publics (ns-publics ns)]
      (update-vals publics #(sci/copy-var* % binding))))
  (sci/init {:namespaces
              {'hiccup2.core (copy-ns 'hiccup2.core)}})

borkdude 2025-07-20T21:12:40.974889Z

you may just want to copy the configuration from here, since hiccup also is added to babashka via SCI: https://github.com/babashka/babashka/blob/master/feature-hiccup/babashka/impl/hiccup.clj

jyn 2025-07-20T21:13:02.483499Z

well, i would like this to work for other libraries besides hiccup

jyn 2025-07-20T21:13:28.294619Z

and it's hard to test those exhaustively because this runtime error only shows up if the guest code actually calls the function that needs the namespace

borkdude 2025-07-20T21:14:17.839959Z

> but it keeps failing because it wants things like hiccup.util that aren't loaded If a macro expands to a call to hiccup.util you need to make sure that namespace also exists in your SCI config

jyn 2025-07-20T21:15:31.103519Z

right. my question is how to get an exhaustive list of namespaces i need, since i don't control all the code that is being run in the SCI interpreter. a friend pointed me to loaded-libs and i had the thought to call that before and after (require) in the host and see how they differ

borkdude 2025-07-20T21:17:36.150049Z

how can you know an exhaustive list of things you need if you don't know what is getting run

jyn 2025-07-20T21:20:04.476679Z

well, i know which namespaces i want to load. if someone tries to use hiccup and it gives an error because i didn't add it to the interpreter, that's ok, they will see a reasonable error message. but if i add hiccup2.core and not hiccup.util a) the errors are much worse and b) that's always wrong, i know statically that i wanted to load hiccup and all things it depends on

borkdude 2025-07-20T21:21:00.254309Z

it depends right. if the public API you want to expose is just hiccup(2).core, then you don't need to add hiccup.util I guess? the fact that it's loaded in the host is kind of irrelevant to that

jyn 2025-07-20T21:21:14.347239Z

no, this is true even if hiccup.util is not public api

jyn 2025-07-20T21:21:17.875269Z

let me show you the error i just got

jyn 2025-07-20T21:21:34.407869Z

Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:46).
Could not resolve symbol: hiccup.util/raw-string

jyn 2025-07-20T21:21:44.112449Z

this is in a call to hiccup/html in the interpreter

jyn 2025-07-20T21:22:03.442279Z

hiccup/html resolved ok, but the functions it expands to aren't present

borkdude 2025-07-20T21:22:17.808389Z

is hiccup/html hiccup2/html or hiccup1/html

jyn 2025-07-20T21:22:25.449619Z

hiccup2.core/html

borkdude 2025-07-20T21:23:14.573399Z

yes, you added a macro which expands to a call in hiccup.util, so you need to add that namespace. but this isn't always the case in general. with macros, yes

borkdude 2025-07-20T21:24:06.103649Z

it's just a matter of testing, I can't give you a general recipe for this. most exposed things in babashka are tested exhaustively via the unit tests of the libraries

jyn 2025-07-20T21:24:24.207819Z

hm, ok

jyn 2025-07-20T21:24:35.797069Z

i will try the loaded-libs thing for now and attack everything with tests to be safe

borkdude 2025-07-20T21:26:06.042839Z

note that there is also a complexity about dynamic vars. sometimes you need to wrap an existing function and bind the host vars to the values of the SCI dynamic vars, before calling the host function: https://github.com/babashka/babashka/blob/bd20a5f973fcf1ed33131eb1079927be9de0d1d8/feature-hiccup/babashka/impl/hiccup.clj#L70-L76

jyn 2025-07-20T21:26:48.039619Z

oh gosh

jyn 2025-07-20T21:27:18.000859Z

and this is required for all functions that use dynamic vars?

borkdude 2025-07-20T21:28:12.018409Z

if you expose the dynamic var to the user, yes

jyn 2025-07-20T21:30:45.434929Z

i am confused. let me restate the question. that render-html function uses a util/*html-mode* binding. what happens if i do a naive thing where i use {:bindings {'render-html h/render-html}} and then call it inside the interpreter? does it error that *html-mode* isn't bound in the interpreter? does it allow assigning to the guest var, but still use the host var regardless?

borkdude 2025-07-20T21:32:14.770209Z

the issue is that the host function isn't aware of the SCI vars

jyn 2025-07-20T21:33:44.986029Z

right. i'm asking whether "expose the dynamic var to the user" means directly, though a public api, or indirectly through some other function that happens to use that dynamic var

jyn 2025-07-20T21:34:58.314359Z

i don't see *html-mode* in hiccup's public api docs, but babashka is still binding it, so i suspect this is true even if it's used indirectly

borkdude 2025-07-20T21:36:19.417269Z

the html-mode dyn var is bound in the html macro

jyn 2025-07-20T21:39:01.986249Z

ok, i think i see

jyn 2025-07-20T21:39:21.742409Z

and in the snippet you linked earlier, you are binding the current value of *html-mode*, that's what @ does?

borkdude 2025-07-20T21:39:45.877149Z

a SCI var is a defef-able thing, so you get the current value of it

borkdude 2025-07-20T21:39:57.599289Z

in the host environment

jyn 2025-07-20T21:40:03.000539Z

cool

borkdude 2025-07-20T21:40:49.516409Z

it would be easier if SCI allowed working with host vars directly, but one of its features is sandboxing which unfortunately makes this a bit more work

👍 1
jyn 2025-07-20T21:41:53.327719Z

i'm still trying to understand the semantics. so html-mode in the guest is initialized to the current value in the host. presumably it can be modified in the guest. and then when the guest exits, all the changes are discarded, the host value stays the same

borkdude 2025-07-20T21:42:37.247339Z

the host value always stays the same, it's never affected by the guest, except in this wrapper function, where I bind the host vars to the guest's current value before calling the host function

jyn 2025-07-20T21:43:08.909569Z

ohhh right because render-html is a host function and sees the host's env

borkdude 2025-07-20T21:43:16.591519Z

exactly

jyn 2025-07-20T21:43:33.449519Z

this is very helpful, thank you

👍 1