sci

https://github.com/babashka/SCI - also see #babashka and #nbb
borkdude 2020-09-12T12:27:39.007Z

@djblue I think I've got IDeref working now in sci and bb (both branches are called IDeref):

user=> (defrecord Foo [x] clojure.lang.IDeref (deref [this] (prn :deref!) x))
user=> @(->Foo :x)
:deref!
:x
This warrants some refactoring/renaming but that's mainly just cleaning up

πŸŽ‰ 1
borkdude 2020-09-13T21:40:33.052100Z

@djblue I just pushed IAtom to babashka / sci (both still in the IDeref branch):

$ rlwrap lein bb
Babashka v0.2.1-SNAPSHOT REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.

user=> (def x (reify clojure.lang.IAtom (swap [_ f] (f 1))))
#'user/x
user=> (swap! x inc)
2

πŸ’― 1
djblue 2020-09-13T22:01:56.052400Z

reify works for me, but I can't get defrecord working

djblue 2020-09-13T22:02:10.052600Z

Also, is it possible to implement both interfaces at the same time?

borkdude 2020-09-13T22:05:11.052900Z

I have tests for defrecord, so in theory it should work. Also implementing both interfaces should also work, but there could be bugs still

borkdude 2020-09-13T22:05:44.053100Z

Maybe you can make a repro for me and post it in the issue: https://github.com/borkdude/sci/issues/401

djblue 2020-09-13T22:06:06.053500Z

Will do!

borkdude 2020-09-13T22:12:23.054Z

Ah, I didn't yet add compareAndSet

djblue 2020-09-13T22:13:55.054200Z

If I remove compareAndSet, it still happens

borkdude 2020-09-13T22:16:14.054400Z

Are you sure you updated the sci branch as well?

borkdude 2020-09-13T22:17:13.054600Z

also run lein clean if you have done any graalvm compilation

djblue 2020-09-13T22:17:19.054800Z

So I cd into the sci directory of babashka and pulled the IDeref branch (fe4637c821fea0d5972c5b99fa6279ef79d3f1ad)

djblue 2020-09-13T22:17:23.055Z

ohh, will do then

borkdude 2020-09-13T22:19:01.055200Z

Anyway, I'm getting errors on swap as well. I'll fix it tomorrow, thanks for the repro

borkdude 2020-09-13T22:19:12.055400Z

deref and reset do work for me

πŸ‘ 1
djblue 2020-09-13T22:19:34.055700Z

I'll see what I'm doing wrong on my end πŸ‘Œ

djblue 2020-09-13T22:19:55.056Z

Thanks for all the hard work!

borkdude 2020-09-13T22:20:25.056200Z

It's a nice challenge

borkdude 2020-09-13T22:25:23.056400Z

Ah I see what's wrong.

borkdude 2020-09-13T22:25:31.056600Z

This works:

(defrecord Example []
  clojure.lang.IDeref
  (deref [this] :deref)

  clojure.lang.IAtom
  (reset [this new-value] :reset)

  (swap  [this f]          :swap)
  #_(swap  [this f a]        :swap)
  #_(swap  [this f a b]      :swap)
  #_(swap  [this f a b args] :swap)

  #_(compareAndSet [this oldv newv] :compare-and-set))

(prn @(->Example))
(prn (reset! (->Example) 1))
(prn (swap! (->Example) inc))

borkdude 2020-09-13T22:26:18.057Z

But there is a problem with multi-arity implementations :)

borkdude 2020-09-14T09:37:23.057400Z

Should be fixed now

djblue 2020-09-14T16:57:18.057600Z

Pulled down the osx bb binary and everything is working perfectly!

borkdude 2020-09-12T12:30:48.007500Z

Should also work in CLJS:

cljs.user=> (sci/eval-string "(defrecord Foo [x] IDeref (-deref [this] (prn :deref!) x)) @(->Foo :x)" {:bindings {'prn prn}})
:deref!
:x

πŸŽ‰ 1
2020-09-12T13:53:20.009700Z

Hello, I am wondering if there is a reason why the sci's only api function is eval-string ? It seems I can do:

(defn my-eval-form [form opts]
  (let [ctxt (opts/init opts)
        ctxt (assoc ctxt :id (or (:id ctxt) (gensym)))]
    (vars/with-bindings {vars/current-ns @vars/current-ns}
      (eval-form ctxt form))))
Is that a bad idea?

borkdude 2020-09-12T13:57:41.010600Z

@jeremys that will only work for some code, e.g. not for code that contains certain aliases, unless the aliases in your form correspond to the aliases in sci

borkdude 2020-09-12T13:58:48.011300Z

I've been thinking of exposing the sci parser as well, so you can first parse a string and then feed it to eval-form. This is what you usually need to do in a REPL.

borkdude 2020-09-12T14:01:39.011800Z

A workaround is to first pr-str your form and then feed it to eval-string

borkdude 2020-09-12T14:02:10.012500Z

Random forms don't have metadata that sci needs for locations in error locations, which is another problem

borkdude 2020-09-12T14:03:34.013600Z

but other than that, there's no real downsize I think

borkdude 2020-09-12T14:04:22.014600Z

note that all sci.impl namespaces are implementation details that might change, they are not a public API

borkdude 2020-09-12T14:06:00.015600Z

If you can explain the background of your question/problem, that would maybe help improving the API

2020-09-12T14:06:56.016300Z

Ok thanks, the thing is I am working on a https://github.com/JeremS/textp-reader and my reader returns clojure data directly.

borkdude 2020-09-12T14:10:42.019100Z

maybe your reader could just return strings instead?

2020-09-12T14:11:32.019500Z

yes it's a possibility I can explore.

borkdude 2020-09-12T14:11:34.019700Z

which you can then evaluate either using clojure or sci

borkdude 2020-09-12T14:12:01.020100Z

clojure has load-string as the equivalent api

borkdude 2020-09-12T14:16:05.022300Z

tools reader also has read+string which will also return the string

2020-09-12T14:16:21.022400Z

I'll take a look at load-string too. I haven't looked very long at sci's code and I didn't know that metadata is used on the evald forms. Although it makes sense...

borkdude 2020-09-12T14:16:52.022700Z

under the hood it uses this clojure parser for location metadata: https://github.com/borkdude/edamame

2020-09-12T14:21:06.024800Z

I use tools.reader to read the actual forms. May be I could use edamame to have the correct metadata, and feed the forms as data.

borkdude 2020-09-12T14:21:36.025600Z

yeah. edamame also uses tools.reader under the hood btw

borkdude 2020-09-12T14:22:00.026300Z

but this is why I was suggesting exposing the sci reader, since it works together with the sci ctx and will set the right aliases based on your ns form etc

borkdude 2020-09-12T14:22:41.027100Z

so you could do:

(def ctx (sci/init opts))
(def form (sci/parse-next ctx stream))
(def result (sci/eval ctx form))

borkdude 2020-09-12T14:25:52.030500Z

a similar thing is needed when you want to implement your own REPL based on sci: https://github.com/borkdude/babashka/blob/3a06297e5ce58f87309df86d9d5759d830b82275/src/babashka/impl/repl.clj#L46 so this needs to be exposed anyway (cc @plexus)

borkdude 2020-09-12T14:28:57.031900Z

but if you don't use any aliases or namespaces then feeding edamame stuff in it would also work

2020-09-12T14:29:20.032Z

yeah the way my reader works is in 2 phases, instaparse separates plain text from clojure text. The clojure text is then fed to tools.reader to get a form. From there I have a list of edn data that's good to be evaled in sequence. Now I'll see If I can used edamame to read he forms instead of tools reader.

borkdude 2020-09-12T14:29:52.032400Z

that should work yes

2020-09-12T14:31:12.033600Z

I'll try different approaches later. The parser allows for stuff like

β—Š(defn template [x]
   β—Šdiv  [:class β—Štext{class1 class2} ]
   {
     Some text and crazy recursion: β—Š(str (+ 2 x) β—Štext{times.})β—Š
   })β—Š

2020-09-12T14:31:28.033800Z

to return

2020-09-12T14:31:34.034100Z

[(defn
  template
  [x]
  (div
   {:tag :tag-args-clj,
    :content
    [:class (text {:tag :tag-args-txt, :content ["class1 class2"]})]}
   {:tag :tag-args-txt,
    :content
    ["\n     Some text and crazy recursion: "
     (str (+ 2 x) (text {:tag :tag-args-txt, :content ["times."]}))
     "\n   "]}))]

borkdude 2020-09-12T14:32:11.034700Z

the diamonds I find a bit confusing, why not just parens? but I'll leave that up to you

2020-09-12T14:35:15.036600Z

the idea comes from https://docs.racket-lang.org/pollen/ which is derived from https://docs.racket-lang.org/pollen/. The β—Š char is used in pollen... Well I didn't just copy the character, it has the advantage of having no meaning in clojure proper.

borkdude 2020-09-12T14:36:06.037200Z

you could also consider using reader tags

2020-09-12T14:36:21.037600Z

The basic Idea is that you could have something like latex exept clojure would be used instead of latex...

borkdude 2020-09-12T14:36:49.038Z

org-mode also has something like this I think

2020-09-12T14:38:33.039400Z

kinda, I don't know or-mode enough.

2020-09-12T14:39:35.040200Z

I gotta go, if you want I let you know how my experiments go. Thank you for the help!

borkdude 2020-09-12T14:47:50.040400Z

yes, please keep me updated

borkdude 2020-09-12T15:08:38.040900Z

@jeremys I exposed a preliminary API with commit 7894773dd071e010483b220d135c1a935a679458:

user=> (require '[sci.core :as sci] :reload-all)
nil
user=> (def ctx (sci/init {}))
#'user/ctx
user=> (def form (sci/parse-string ctx "(+ 1 2 3)"))
#'user/form
user=> form
(+ 1 2 3)
user=> (sci/eval ctx form)
6
user=> (def multiple-forms "(+ 1 2 3) (+ 4 5 6)")
#'user/multiple-forms
user=> (def reader (sci/reader multiple-forms))
#'user/reader
user=> (def next-form (sci/parse-next ctx reader))
#'user/next-form
user=> next-form
(+ 1 2 3)
user=> (sci/eval ctx next-form)
6
user=> (def next-form (sci/parse-next ctx reader))
#'user/next-form
user=> next-form
(+ 4 5 6)
user=> (sci/eval ctx next-form)
15
user=> (def next-form (sci/parse-next ctx reader))
#'user/next-form
user=> next-form
:sci.core/eof

borkdude 2020-09-12T15:08:52.041200Z

Feel free to experiment with this and post feedback

borkdude 2020-09-12T15:08:59.041400Z

This is in sci

borkdude 2020-09-12T15:34:50.041600Z

Also made this issue: https://github.com/borkdude/sci/issues/404

djblue 2020-09-12T18:21:39.042Z

This is awesome, thanks for getting it working!

borkdude 2020-09-12T18:22:23.042300Z

I guess next is IAtom and IAtom2? ;)

djblue 2020-09-12T18:24:28.042500Z

This is how I'm using it https://github.com/djblue/portal/blob/master/src/portal/runtime/client/jvm.clj#L15

djblue 2020-09-12T18:25:22.042800Z

So IAtom would be enough for my use case

djblue 2020-09-12T18:25:32.043Z

I didn't even know about IAtom2

borkdude 2020-09-12T18:26:53.043500Z

swap-vals

djblue 2020-09-12T18:28:51.043700Z

Ohh it's like swap!, but returns [old new] πŸ‘Œ

djblue 2020-09-12T18:29:25.043900Z

I'm gonna pull down the branch and see what happens

borkdude 2020-09-12T18:29:43.044100Z

yeah, it will probably fail on IAtom now

borkdude 2020-09-12T18:29:47.044300Z

but IDeref should work

borkdude 2020-09-12T18:30:00.044500Z

you can use .cljc and use #?(:bb ...)

πŸ‘ 1
2020-09-12T19:39:55.049600Z

@borkdude I made some quick tests with eval, it seems like I can eval the code I'd like to without changing my reader. I still have to put a coherent API together but it seems like sci will work wonders for what I have in mind. I'll report back when it's done. Thanks!

borkdude 2020-09-12T21:59:25.050100Z

Added a nodeJS REPL test example https://github.com/borkdude/sci/blob/d0543150e1493abcb29c8c9aaf743b1622715925/test-resources/sci/examples/repl.cljs

πŸŽ‰ 3
djblue 2020-09-12T22:17:13.050400Z

This is really cool!