Fork me on GitHub
#sci
<
2022-04-25
>
pmooser10:04:42

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.

borkdude10:04:27

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

pmooser10:04:50

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

borkdude10:04:17

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

pmooser10:04:11

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!

pmooser13:04:27

@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 ?

borkdude13:04:50

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

borkdude13:04:10

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

borkdude13:04:18

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

pmooser14:04:42

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.

borkdude14:04:45

So

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

pmooser14:04:58

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

pmooser14:04:07

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

borkdude14:04:25

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

borkdude14:04:38

you can try to :exclude those by name

borkdude14:04:19

I also know what the issue is

pmooser14:04:26

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

borkdude14:04:28

but haven't gotten around to fixing that yet

pmooser14:04:46

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

borkdude14:04:57

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

borkdude14:04:48

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

pmooser14:04:52

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.

pmooser14:04:12

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

borkdude14:04:38

With "not supported in source" I mean:

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

pmooser14:04:20

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

borkdude14:04:30

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

borkdude14:04:23

@pmooser which version of SCI are you using?

borkdude14:04:54

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}

borkdude14:04:43

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

pmooser14:04:33

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

pmooser14:04:40

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

pmooser14:04:05

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.

pmooser14:04:54

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

pmooser14:04:02

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.

borkdude14:04:00

@pmooser Should be fixed with 03819bad08547a426c041e4031010f4ef75dfc0b

borkdude14:04:22

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

borkdude14:04:35

and when copying vars SCI expected that to be there

borkdude14:04:42

and this is why there was an NPE

borkdude14:04:23

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

pmooser14:04:39

@borkdude Here is a minimal reproduction:

(ns my.ns)

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

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

pmooser14:04:51

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

pmooser14:04:31

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

borkdude14:04:41

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}

pmooser14:04:18

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

borkdude14:04:42

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

pmooser14:04:02

perfect thanks

pmooser14:04:10

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

pmooser14:04:17

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"}

borkdude14:04:00

it seems you have both SCI versions on the classpath

pmooser14:04:36

Yeah I'm working on fixing that ...

pmooser14:04:50

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

pmooser14:04:14

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

pmooser15:04:33

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?

borkdude15:04:36

@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

borkdude15:04:48

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 ...)}}})

pmooser15:04:57

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

borkdude15:04:23

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

borkdude16:04:01

thanks for responding in private

pmooser16:04:34

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.

pmooser16:04:41

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

🙏 1
devn17:04:26

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?

devn17:04:42

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

borkdude18:04:46

@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

borkdude18:04:06

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

devn18:04:58

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

borkdude18:04:17

@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 ....)

devn18:04:32

great, thanks

devn18:04:50

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

devn18:04:03

for instance, saw immediately that time is not in there

borkdude18:04:54

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

borkdude18:04:37

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

borkdude18:04:26

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

devn18:04:42

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”

borkdude18:04:54

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

devn18:04:24

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

devn18:04:05

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

borkdude18:04:24

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

devn18:04:36

d’oh, right, thank you

devn18:04:08

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

devn18:04:26

nvm got it

borkdude18:04:36

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

devn18:04:06

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

devn18:04:42

err after eval’ing it of course…

borkdude18:04:56

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

👍 1