@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@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
reify works for me, but I can't get defrecord working
Also, is it possible to implement both interfaces at the same time?
I have tests for defrecord, so in theory it should work. Also implementing both interfaces should also work, but there could be bugs still
Maybe you can make a repro for me and post it in the issue: https://github.com/borkdude/sci/issues/401
Will do!
https://github.com/borkdude/sci/issues/401#issuecomment-691732506
Ah, I didn't yet add compareAndSet
If I remove compareAndSet, it still happens
Are you sure you updated the sci branch as well?
also run lein clean
if you have done any graalvm compilation
So I cd into the sci directory of babashka and pulled the IDeref branch (fe4637c821fea0d5972c5b99fa6279ef79d3f1ad)
ohh, will do then
Anyway, I'm getting errors on swap as well. I'll fix it tomorrow, thanks for the repro
deref and reset do work for me
I'll see what I'm doing wrong on my end π
Thanks for all the hard work!
It's a nice challenge
Ah I see what's wrong.
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))
But there is a problem with multi-arity implementations :)
Should be fixed now
Pulled down the osx bb binary and everything is working perfectly!
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
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?@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
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.
A workaround is to first pr-str your form and then feed it to eval-string
Random forms don't have metadata that sci needs for locations in error locations, which is another problem
but other than that, there's no real downsize I think
note that all sci.impl namespaces are implementation details that might change, they are not a public API
If you can explain the background of your question/problem, that would maybe help improving the API
Ok thanks, the thing is I am working on a https://github.com/JeremS/textp-reader and my reader returns clojure data directly.
maybe your reader could just return strings instead?
yes it's a possibility I can explore.
which you can then evaluate either using clojure or sci
clojure has load-string
as the equivalent api
tools reader also has read+string which will also return the string
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...
under the hood it uses this clojure parser for location metadata: https://github.com/borkdude/edamame
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.
yeah. edamame also uses tools.reader under the hood btw
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
so you could do:
(def ctx (sci/init opts))
(def form (sci/parse-next ctx stream))
(def result (sci/eval ctx form))
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)
but if you don't use any aliases or namespaces then feeding edamame stuff in it would also work
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.
that should work yes
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.})β
})β
to return
[(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 "]}))]
the diamonds I find a bit confusing, why not just parens? but I'll leave that up to you
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.
you could also consider using reader tags
The basic Idea is that you could have something like latex exept clojure would be used instead of latex...
org-mode also has something like this I think
kinda, I don't know or-mode enough.
I gotta go, if you want I let you know how my experiments go. Thank you for the help!
yes, please keep me updated
@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
Feel free to experiment with this and post feedback
This is in sci
Also made this issue: https://github.com/borkdude/sci/issues/404
This is awesome, thanks for getting it working!
I guess next is IAtom and IAtom2? ;)
This is how I'm using it https://github.com/djblue/portal/blob/master/src/portal/runtime/client/jvm.clj#L15
So IAtom would be enough for my use case
I didn't even know about IAtom2
What is https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IAtom2.java#L13 used for?
swap-vals
Ohh it's like swap!, but returns [old new] π
I'm gonna pull down the branch and see what happens
yeah, it will probably fail on IAtom now
but IDeref should work
you can use .cljc and use #?(:bb ...)
@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!
Added a nodeJS REPL test example https://github.com/borkdude/sci/blob/d0543150e1493abcb29c8c9aaf743b1622715925/test-resources/sci/examples/repl.cljs
This is really cool!