sci

2022-04-25T10:14:42.526749Z

I'm trying to map my namespaces to sci namespaces automatically, but I'm messing something up: (def sci-opts {:namespaces {'user (copy-ns 'user (sci/create-ns 'user))}}) Any pointers? I'm sure I'm doing something obviously stupid.

borkdude 2022-04-25T10:16:27.892479Z

you don't need the quote in 'user in the first arg of copy-ns

2022-04-25T10:16:50.136319Z

Allright, that should have been clear to me from the assert! Thank you.

borkdude 2022-04-25T10:18:17.355639Z

although we could allow that in the macro to just take the second element if the argument is (quote user)

2022-04-25T10:21:11.465689Z

Well, I don't think there's necessarily any problem with the existing API. I think it's just that I don't have an example to reference, and I'm porting some existing code from using the self-hosted cljs to sci, so honestly things are a bit chaotic. But I'm hopefully almost there!

2022-04-25T13:58:27.647489Z

@borkdude So I see that sci doesn't (yet) support deftype - what will happen if you load a namespace that directly or indirectly uses deftype ?

borkdude 2022-04-25T13:58:50.830169Z

@pmooser That's not a problem. You just cannot (at the moment) execute deftype from source

borkdude 2022-04-25T13:59:10.125409Z

But SCI can re-use anything that's being brought in via functions in the SCI config

borkdude 2022-04-25T13:59:18.913929Z

because it just calls those functions, it's not any magic :)

2022-04-25T14:00:42.293519Z

So I'm still on my journey of playing with copy-ns and see if I can replace my use of self-hosted cljs. I'm noticing that with a specific namespace only, when I try to use copy-ns, I get an error, and I was wondering if that could be caused by trying to copy a namespace containing a deftype.

borkdude 2022-04-25T14:00:45.552589Z

So

(deftype Foo [])
(defn foo [] (->Foo))
and
(sci/eval-string "(foo)" {:namespaces {'user {'foo foo}}})
just works.

2022-04-25T14:00:58.476529Z

Encountered error when macroexpanding sci.core/copy-ns.
NullPointerException: 

2022-04-25T14:01:07.380619Z

(I realize that's not a very helpful stack or error I just pasted)

borkdude 2022-04-25T14:01:25.465709Z

@pmooser I've had some of these issues too, it usually happens when there are protocols in that namespace.

borkdude 2022-04-25T14:01:38.897829Z

you can try to :exclude those by name

borkdude 2022-04-25T14:02:19.902819Z

I also know what the issue is

2022-04-25T14:02:26.285299Z

Ah yes, this namespace in particular contains a number of protocols.

borkdude 2022-04-25T14:02:28.697949Z

but haven't gotten around to fixing that yet

2022-04-25T14:02:46.170319Z

So if I can ask, how does your foo example you pasted above work, if sci doesn't support deftype?

borkdude 2022-04-25T14:02:57.562349Z

you can also just use copy-var - it's a bit more boilerplate, but should work more robustly

borkdude 2022-04-25T14:03:48.532609Z

yes, because SCI has a reference to the foo function, it can just call that and then the rest within that function call isn't interpreted anymore, it's compiled code, so SCI isn't involved in that

2022-04-25T14:03:52.407189Z

Well, in this particular case, this namespace exports relatively few things, and the useful ones are things that return deftype instances, so that sounds like maybe the only real solution is to omit the namespaces, unless I'm not understanding what is happening.

2022-04-25T14:04:00.857809Z

Ahhhh ok.

2022-04-25T14:04:12.671799Z

So even though it doesn't support it, it might be able to call it, but not in some kind of dynamic fashion.

borkdude 2022-04-25T14:04:38.064839Z

With "not supported in source" I mean:

(sci/eval-string "(deftype Foo [])")

2022-04-25T14:05:20.368849Z

Sure, ok. I apologize if my questions are a bit repetitive - I'm just trying to wrap my head around it.

borkdude 2022-04-25T14:05:27.564939Z

no problem

borkdude 2022-04-25T14:11:30.250689Z

I'd be interested in a repro of copy-ns + protocol behavior, so it can be fixed though

borkdude 2022-04-25T14:12:23.872489Z

@pmooser which version of SCI are you using?

borkdude 2022-04-25T14:13:54.017369Z

I can't reproduce it like this:

foo=> (defprotocol Foo (foo [this]))
foo=> (require '[sci.core :as sci])
nil
foo=> (sci/copy-ns foo (sci/create-ns 'foo))
{Foo #'Foo, foo #'foo}

borkdude 2022-04-25T14:14:43.312009Z

Ah:

foo=> (deftype Dude [])
foo/Dude
foo=> (sci/copy-ns foo (sci/create-ns 'foo))
Unexpected error (NullPointerException) macroexpanding sci.core/copy-ns at (<cljs repl>:1:1).
null

2022-04-25T14:16:33.100269Z

@borkdude 0.3.3 which looks like I'm out of date by one revision.

2022-04-25T14:16:40.562319Z

I'll update to 0.3.4 and see what I can do.

2022-04-25T14:17:05.857209Z

I didn't have any luck with :exclude and copy-ns yet - I tried just listing all my protocols and types but it didn't seem to work.

2022-04-25T14:20:54.635599Z

Sorry, I'm wrong - I'm already on 0.3.4.

2022-04-25T14:23:02.230069Z

I'm trying to figure out how to reproduce it, since I can't just start commenting out pieces of it, since it's a vital part of my program. Ah, let me try physically copying that ns to another file, seeing if I can reproduce it there, and just start nuking parts of the file until the error goes away.

borkdude 2022-04-25T14:25:00.465899Z

@pmooser Should be fixed with 03819bad08547a426c041e4031010f4ef75dfc0b

borkdude 2022-04-25T14:25:22.337509Z

It was a deftype "var" in CLJS which has no :meta field

borkdude 2022-04-25T14:25:35.190079Z

and when copying vars SCI expected that to be there

borkdude 2022-04-25T14:25:42.393869Z

and this is why there was an NPE

borkdude 2022-04-25T14:27:23.957969Z

@pmooser could you try that commit? (assuming you use deps.edn)

2022-04-25T14:32:39.474189Z

@borkdude Here is a minimal reproduction:

(ns my.ns)

(defprotocol PFoo
  (do-foo [this]))

(defrecord Foo [arg]
    PFoo
    (do-foo [this] 100))

2022-04-25T14:32:51.422869Z

It doesn't like the defrecord there and I'm not sure the protocol has anything to do with it.

2022-04-25T14:33:31.425909Z

Ok, I can see if I can that try that new SHA ...

borkdude 2022-04-25T14:33:41.880029Z

ok, now works:

ClojureScript 1.10.914
cljs.user=> (ns my.ns)

(defprotocol PFoo
  (do-foo [this]))

(defrecord Foo [arg]
    PFoo
    (do-foo [this] 100))
nil
my.ns=> my.ns=> false
my.ns=> my.ns=> my.ns/Foo
my.ns=> (require '[sci.core :as sci])
nil
my.ns=> (sci/copy-ns my.ns (sci/create-ns 'my.ns))
{PFoo #'my.ns/PFoo, do-foo #'my.ns/do-foo, ->Foo #'my.ns/->Foo, map->Foo #'my.ns/map->Foo}

2022-04-25T14:34:18.585889Z

Great. Trying the new sha, just gotta remember the syntax for deps

borkdude 2022-04-25T14:34:42.273909Z

{:git/url "" :git/sha "03819bad08547a426c041e4031010f4ef75dfc0b"}

2022-04-25T14:35:02.962039Z

perfect thanks

2022-04-25T14:37:10.450499Z

Sorry, I'm having some kind of issue (not related to the new version, just related to getting it):

2022-04-25T14:37:17.933679Z

Cloning: 
Checking out:  at 03819bad08547a426c041e4031010f4ef75dfc0b
Error building classpath. Unable to compare versions for org.babashka/sci: {:mvn/version "0.3.4", :deps/manifest :mvn} and {:git/url "", :git/sha "03819bad08547a426c041e4031010f4ef75dfc0b", :deps/manifest :deps, :deps/root "/Users/pmooser/.gitlibs/libs/org.babashka/sci/03819bad08547a426c041e4031010f4ef75dfc0b"}

borkdude 2022-04-25T14:38:00.459699Z

it seems you have both SCI versions on the classpath

2022-04-25T14:38:36.660279Z

Yeah I'm working on fixing that ...

2022-04-25T14:38:50.951329Z

Ok, now moving on to testing the new version ..

2022-04-25T14:40:14.168599Z

@borkdude Nice job, it looks like it is fixed.

borkdude 2022-04-25T14:40:19.912239Z

🎉

2022-04-25T15:33:33.571599Z

Do you have any advice for using this in the context of interactive development, like with shadow-cljs and hot code reloading? Is there a way to keep the existing sci environment but maybe update the namespace mappings?

borkdude 2022-04-25T15:35:36.568269Z

@pmooser Yes: • create a sci context with sci/init using defonce and store that context in a mutable ref (atom or volatile) • evaluate using eval-string* + that context • in the REPL, update mapping using sci/merge-opts and swap! the new context into the mutable ref

borkdude 2022-04-25T15:36:48.099269Z

So something like:

(defonce sci-ctx (atom (sci/init {...initial options ...}))
(eval-string* @sci-ctx "(+ 1 2 3)")
(swap! sci-ctx sci/merge-opts {:namespaces {'foo {'bar (fn ...)}}})

2022-04-25T15:39:57.067489Z

Ok, great - merge-opts is what I was missing. Thanks again!

borkdude 2022-04-25T15:45:23.755369Z

@pmooser can I ask what type of app you're working on? closed or open source? domain? just curious

borkdude 2022-04-25T16:03:01.023149Z

thanks for responding in private

2022-04-25T16:07:34.590319Z

I think this kind of project is important for the clojure ecosystem. Rich used to talk about clojure-in-clojure, but I think that's kind of never happening, and the self-hosted cljs is such a marginalized experience that any time something weird happens with it and shadow-cljs, immediately all fingers always point to the self-hosted cljs code.

2022-04-25T16:07:41.122479Z

So anyway, thanks for working on it ... it looks good so far.

🙏 1
devn 2022-04-25T17:17:26.722729Z

im looking at SCI as a drop-in replacement for clojail. in the past i used clojail to run expressions from a large collection of #clojure IRC logs in a sandbox with a java security policy in case there was anything malicious. one thing i immediately noticed is that SCI doesn’t have a notion of timeout when eval’ing a string. given a lot of my inputs are things like (map inc (range)) and similar expressions which will run forever, i need some way of short-circuiting if it hasn’t returned in say 10sec. is that something SCI can accommodate?

devn 2022-04-25T17:45:42.299839Z

i guess maybe I could just run things in a thread and set a timeout

borkdude 2022-04-25T18:10:46.361659Z

@devn That's correct, SCI does not have a timeout, you should use a host-specific wrapper for this, e.g. in a JVM use a Thread with .stop or so

borkdude 2022-04-25T18:11:06.518189Z

In Node.js there is "vm" which you can use for this

devn 2022-04-25T18:11:58.877489Z

related: capturing *out* in addition to value is possible right? for example: (time (inc 1)) ’s value and output

borkdude 2022-04-25T18:15:17.363469Z

@devn yes, sci has its own *out* which you can bind to clojure's *out* if you want:

(sci/binding [sci/out *out*] ....)
(sci/with-out-string ....)

devn 2022-04-25T18:15:32.985559Z

great, thanks

devn 2022-04-25T18:15:50.241249Z

clojail does seem quite a bit more permissive, but im going to give SCI a try on the input and see how it works out

devn 2022-04-25T18:16:03.175149Z

for instance, saw immediately that time is not in there

borkdude 2022-04-25T18:16:54.764689Z

time is something we can probably just add. it is available in babashka which uses SCI. you can copy it from there for now. https://github.com/babashka/babashka/blob/9727cabaf3499c7129657cb91b097c92152fe144/src/babashka/impl/clojure/core.clj#L17

borkdude 2022-04-25T18:18:37.430159Z

This also works:

user=> (sci/binding [sci/out *out*] (sci/eval-string "(time (inc 1))" {:classes {'java.lang.System System} :namespaces {'clojure.core {'time (sci/copy-var time (sci/create-ns 'clojure.core))}}}))
"Elapsed time: 0.262606 msecs"
2

borkdude 2022-04-25T18:19:26.082909Z

Ah now I remember why time wasn't added, since it requires java.lang.System ;)

devn 2022-04-25T18:19:59.014499Z

🙂

devn 2022-04-25T18:20:42.240919Z

i jumped in here like “ok, time to reboot this old bit-rotted project” and then got flung into “oh right, what i allow and don’t is actually half the project”

borkdude 2022-04-25T18:23:54.581279Z

@devn what's the project, just out of curiosity?

devn 2022-04-25T18:24:16.809259Z

https://github.com/devn/getclojure

borkdude 2022-04-25T18:25:09.233819Z

Nice!

devn 2022-04-25T18:27:24.177839Z

ok one more question for ya: in your example above where you bind out — i want to produce a map containing the input expr, the value of the expr, and the out value

devn 2022-04-25T18:28:05.831699Z

in the past i did something like bind #'*out* to a StringWriter.

borkdude 2022-04-25T18:28:24.504779Z

(sci/binding [sci/out (new StringWriter)] ...)

devn 2022-04-25T18:28:36.884299Z

d’oh, right, thank you

devn 2022-04-25T18:31:08.615079Z

err i guess i tried that — i expected to be able to call (str sci/out) but that doesn’t seem to be it

devn 2022-04-25T18:32:26.681439Z

nvm got it

borkdude 2022-04-25T18:32:36.632819Z

(let [sw (StringWriter.)] ... (str sw))

devn 2022-04-25T18:33:06.869449Z

(with-open [w (StringWriter.)] (sci/binding [sci/out writer] {:output (str w)}))

devn 2022-04-25T18:33:42.957739Z

err after eval’ing it of course…

borkdude 2022-04-25T18:34:56.315229Z

user=> (with-open [w (java.io.StringWriter.)] (sci/binding [sci/out w] (sci/eval-string "(prn :hello)") {:output (str w)}))
{:output ":hello\n"}

👍 1