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
is this in clojure or clojurescript btw? in JVM clojure you can copy macros with sci/copy-var
clojure, but i want to compile it to a native executable with GraalVM and i'm not sure if copy-var works there
yes, it works also for that
as long as you use copy-var on the top level, not in a function
everything in SCI is designed with graal native-image in mind
oh very cool
i will probably do that then 🙂 thank you!
oh wait apparently load-file was doing caching hm
... oh it works now
huh
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
can you explain how you added hiccup2.core to SCI?
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)}})
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
well, i would like this to work for other libraries besides hiccup
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
> 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
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
how can you know an exhaustive list of things you need if you don't know what is getting run
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
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
no, this is true even if hiccup.util is not public api
let me show you the error i just got
Execution error (ExceptionInfo) at sci.impl.utils/throw-error-with-location (utils.cljc:46).
Could not resolve symbol: hiccup.util/raw-stringthis is in a call to hiccup/html in the interpreter
hiccup/html resolved ok, but the functions it expands to aren't present
is hiccup/html hiccup2/html or hiccup1/html
hiccup2.core/html
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
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
hm, ok
i will try the loaded-libs thing for now and attack everything with tests to be safe
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
oh gosh
and this is required for all functions that use dynamic vars?
if you expose the dynamic var to the user, yes
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?
the issue is that the host function isn't aware of the SCI vars
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
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
the html-mode dyn var is bound in the html macro
ok, i think i see
and in the snippet you linked earlier, you are binding the current value of *html-mode*, that's what @ does?
a SCI var is a defef-able thing, so you get the current value of it
in the host environment
cool
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
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
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
ohhh right because render-html is a host function and sees the host's env
exactly
this is very helpful, thank you