A port of Clojurescript Koans using SCI. Thank you, @borkdude, to make that possible. gratitude
Amazing work, thank you
I have a confession, I tried to change as little as possible, so I first attempted to use self-hosted Clojurescript. But after trying to make it work using an updated Clojurescript and shadow-cljs I gave it up and decided to port to SCI. It took less time to convert to SCI than the time I used to try to use self-hosting. Also, I can use advanced compilation, and the SCI version is way smaller than the Clojurescript one. SCI is an incredible tool.
great to hear. there's another option emerging for this kind of use case, it's the cherry compiler that can be embedded: https://github.com/squint-cljs/cherry/blob/main/doc/embed.md but it's less mature than SCI (simply because it's newer) - the only benefit of that is better performance (which should not matter for a tool that teaches how to use Clojure)
More options are great! I will check it out, but the current bundle size is enough, so I will probably keep on the more mature tool. Does SCI track clojure or clojurescript when there are different behaviors? I found some differences from clojure while I was writing some lessons on another project.
SCI doesn't track clojure or clojurescript unless there are new behaviors like the one with keyword arguments vs passing a map. This is so that SCI can be used with older version of Clojure as well. Right now it supports 1.9
Which differences with Clojure did you find?
Only corner cases, like calling max without arguments. Cases where Clojure returns exceptions, I think. I haven't checked the Clojurescript behavior yet to confirm that it is the same.
The max function isn't re-implemented so that would surprise me
$ bb -e '(max)'
----- Error --------------------------------------------------------------------
Type: clojure.lang.ArityException
Message: Wrong number of args (0) passed to: clojure.core/maxThe CLJS version only throws because max in call position is implemented using a macro. What happens in SCI is more like:
plk -e '(let [m max] (m))'which doesn't throw
Let me pick a real one instead of trying to use my memory 😅
subs throws in clojure with index out of bounds when using an index larger than the string size.
also not re-implemented. it's probably just a CLJS vs CLJ difference
Ok, so when are cases like this it should behave like Clojurescript except when there is a macro implementation?
yes, test it like: (let [f subs] (f ...))
btw in CLJS:
$ plk -e '(subs "" 0 11)'
""of course if you wanted to, you could override subs in clojure.core with your own:
{:namespaces {'clojure.core {'subs ...}}}`Yeah, I suspected that was the case (that it behaved like in cljs). Thank you, I need to think if I would go that far, not really sure if it is necessary. I would just show an example throwing, so the user would not be surprised when it happened. There will be other exceptions that I can use 😉
Are parse-* not available by default for sci/eval-string?
what do you mean exactly, parse-what?
Sorry. parse-long parse-double
ah gotcha. not by default since sci goes back to clojure 1.9. pretty easy to add yourself though
yep, ok thanks. Is there a writeup somewhere about what things went into (or didn't) sci defaults and why?
it would be good for me to respect some of the same decisions, but I don't have the background rationale
added the parse-* just now. was indeed trivial 🙂
It just supports the functions that are in 1.9 and doesn’t support any functions that are deemed unsafe for sandboxing
Like spit etc
yeah, that's mostly what I wish I knew. what's safe to eval wrt arbitrary input. did you just figure that out over time?
you can take a look at what babashka adds compared to SCI and then you'll know
ok