sci

https://github.com/babashka/SCI - also see #babashka and #nbb
mkvlr 2021-01-24T09:57:51.010Z

I’d like to implement require but lazy load a :namespace map compiled to cljs instead of a Clojure source file, is this possible?

mkvlr 2021-01-24T10:02:57.011300Z

looking at https://github.com/borkdude/sci/blob/19242bf2cf549fd7895696e65761c04e32c61f7f/src/sci/impl/evaluator.cljc#L193 it seems it’s not but seems easy enough to add. Would a PR be welcome for it?

borkdude 2021-01-24T10:48:57.011900Z

@mkvlr Can you sketch the scenario of what should happen on require?

borkdude 2021-01-24T10:56:28.013Z

As long as you return the source string using the :load-fn it should work

mkvlr 2021-01-24T11:00:09.013700Z

@borkdude I want to load an cljs advanced compiled module, basically merge the namespaces map on load

mkvlr 2021-01-24T11:00:20.013900Z

not Clojure source

mkvlr 2021-01-24T11:03:23.015400Z

so basically load https://github.com/sicmutils/sicmutils/blob/master/src/sicmutils/env/sci.cljc#L105 lazily but having it part of my advanced compiled bundle (but as a separate module)

borkdude 2021-01-24T11:05:49.016300Z

@mkvlr ah, yeah, I think you can mis-use :load-fn for this by checking the namespace, then updating your ctx manually and then return an empty source string

borkdude 2021-01-24T11:07:16.017100Z

(update ctx :env swap! assoc-in [:namespaces your-ns-name] your-ns)
something like this

mkvlr 2021-01-24T11:07:40.017500Z

@borkdude thanks, will give it a try and let you know!

mkvlr 2021-01-24T13:06:15.019600Z

@borkdude returning an empty source and changing the env works. But I still need a async variant of :load-fn . https://github.com/borkdude/sci/issues/511

borkdude 2021-01-24T13:08:41.020200Z

@mkvlr if this is going to be async, how do you prevent using the required namespace in successive expressions?

borkdude 2021-01-24T13:10:44.020800Z

I was thinking, maybe you can fire the async operation and then wait in some loop until the async operation has completed?

borkdude 2021-01-24T13:11:01.021100Z

eh, this is JS async hell again

mkvlr 2021-01-24T13:11:14.021300Z

yes

mkvlr 2021-01-24T13:11:33.021700Z

I thought we can contain it but there’s not really a way, right?

mkvlr 2021-01-24T13:11:55.022200Z

it basically means sci/eval has to become async?

borkdude 2021-01-24T13:13:39.022700Z

you are fetching the module via some http request?

mkvlr 2021-01-24T13:13:55.022900Z

yes

borkdude 2021-01-24T13:15:01.023700Z

is something like this not possible? async operation -> marks flag when done loop { wait for flag to become true }

borkdude 2021-01-24T13:18:54.024400Z

we need await in CLJS ;)

borkdude 2021-01-24T13:19:47.025200Z

I'm not sure what the consequences will be if load-fn has an async counterpart. Will then everything become async?

mkvlr 2021-01-24T13:20:41.025700Z

I think so, that’s why its called a leaky abstraction right?

mkvlr 2021-01-24T13:22:55.026700Z

https://eloquentjavascript.net/11_async.html says: > If we had used the handler’s return value as the response value, that would mean that a request handler can’t itself perform asynchronous actions. A function doing asynchronous work typically returns before the work is done, having arranged for a callback to be called when it completes. So we need some asynchronous mechanism—in this case, another callback function—to signal when a response is available.

borkdude 2021-01-24T13:22:56.026800Z

So an alternative would be to include the module inside some dom node or JS variable as a string and load it from there maybe? Or will this have negative consequences?

borkdude 2021-01-24T13:24:15.027700Z

Isn't is possible to get synchronous loading by creating a dom node which refers to that file and then reading the contents from that dom node?

borkdude 2021-01-24T13:24:28.027900Z

(thinking of workarounds)

borkdude 2021-01-24T13:25:37.028500Z

a dom node which does a GET request and gets populated with a string response from your server

borkdude 2021-01-24T13:25:53.029Z

and then that is fed to load-file

borkdude 2021-01-24T13:26:03.029300Z

load-fn rather

mkvlr 2021-01-24T13:26:08.029500Z

I think it’s just not possible to do async work from a non-async function or do I have a fundamental misunderstanding?

borkdude 2021-01-24T13:26:23.029800Z

yes, the workaround I suggest is making everything sync

borkdude 2021-01-24T13:27:07.030Z

https://stackoverflow.com/a/2880147/6264

mkvlr 2021-01-24T13:27:54.030600Z

yikes

borkdude 2021-01-24T13:27:58.030800Z

it will block your UI, but for require I think it's reasonable, since this is what happens in CLJ too

mkvlr 2021-01-24T13:28:17.031300Z

will try it, thanks!

borkdude 2021-01-24T13:28:22.031400Z

they even added serialized-require to CLJ now to make it more synchronous

mkvlr 2021-01-24T13:29:01.031700Z

yeah, but you can still have other threads running in this case?

mkvlr 2021-01-24T13:31:01.032400Z

pretty sure that @thheller made cljs_eval return a promise for the same reason: https://github.com/thheller/shadow-cljs/blob/206d4029a80afa267835782516a9cb8eca92e8bf/src/main/shadow/cljs/devtools/client/shared.cljs#L424-L452

borkdude 2021-01-24T13:32:06.033200Z

of course, but for loading a namespace from an external resource, I don't think it's unreasonable to show a spinner and wait for it to be loaded. it simplifies the model drastically

borkdude 2021-01-24T13:32:37.033500Z

we can make everything async, but this will be a major change

mkvlr 2021-01-24T13:32:44.033700Z

https://stackoverflow.com/a/21730944

mkvlr 2021-01-24T13:32:55.034100Z

this doesn’t sound so good either

borkdude 2021-01-24T13:33:21.034400Z

> Just because appendChild() has returned does not in anyway guarantee that the script has finished executing He is talking about the JS eval

borkdude 2021-01-24T13:33:35.034900Z

but the response is there. We have sync eval in sci

borkdude 2021-01-24T13:33:40.035200Z

so this should work

mkvlr 2021-01-24T13:33:45.035300Z

no, I’m loading a js module

borkdude 2021-01-24T13:34:00.035600Z

😱

mkvlr 2021-01-24T13:34:34.035800Z

hehe

mkvlr 2021-01-24T13:35:29.036800Z

think I need to look at how async could be supported in sci and sleep on it

borkdude 2021-01-24T13:35:30.036900Z

so what would happen if you wrapped the sync sci API functions in your own async functions?

borkdude 2021-01-24T13:35:56.037400Z

so any eval string you do would be async and the string could be coming from an http request?

mkvlr 2021-01-24T13:36:59.038800Z

would that be problematic when you eval code that does the require and uses it in the same cell?

borkdude 2021-01-24T13:38:23.039300Z

maybe it wouldn't be so bad to introduce some async API functions

borkdude 2021-01-24T13:38:32.039600Z

don't know how deep the rabbit hole would be

borkdude 2021-01-24T13:38:43.039900Z

but worth an experiment I'd say

borkdude 2021-01-24T13:38:58.040500Z

probably the analyzer can stay sync

borkdude 2021-01-24T13:39:04.040700Z

also the parser can stay sync

borkdude 2021-01-24T13:39:36.041200Z

I mean, we would add async functions, not remove the sync ones

mkvlr 2021-01-24T13:41:02.041900Z

I’ll give it a try, probably tomorrow

mkvlr 2021-01-24T13:42:12.042700Z

thanks for your help!

borkdude 2021-01-24T13:42:16.042900Z

:thumbsup:

borkdude 2021-01-24T13:45:46.043700Z

@mkvlr btw, I'm not entirely sure about the comment on SO:

eval("1 + 1")
2
this is entirely sync btw, you do need to allow this, many sites forbid it

borkdude 2021-01-24T13:47:17.044Z

but there might be a way to do this safely

borkdude 2021-01-24T13:47:30.044300Z

of course this is hacky, so an async API would be the best

borkdude 2021-01-24T13:48:15.044900Z

but in theory, I think you can do it all synchronously: the request/response + js eval