Fork me on GitHub
#sci
<
2020-09-12
>
borkdude12:09:39

@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

πŸŽ‰ 3
djblue18:09:39

This is awesome, thanks for getting it working!

borkdude18:09:23

I guess next is IAtom and IAtom2? ;)

djblue18:09:22

So IAtom would be enough for my use case

djblue18:09:32

I didn't even know about IAtom2

djblue18:09:51

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

djblue18:09:25

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

borkdude18:09:43

yeah, it will probably fail on IAtom now

borkdude18:09:47

but IDeref should work

borkdude18:09:00

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

πŸ‘ 3
borkdude21:09:33

@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

πŸ’― 3
djblue22:09:56

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

djblue22:09:10

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

borkdude22:09:11

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

borkdude22:09:44

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

borkdude22:09:23

Ah, I didn't yet add compareAndSet

djblue22:09:55

If I remove compareAndSet, it still happens

borkdude22:09:14

Are you sure you updated the sci branch as well?

borkdude22:09:13

also run lein clean if you have done any graalvm compilation

djblue22:09:19

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

djblue22:09:23

ohh, will do then

borkdude22:09:01

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

borkdude22:09:12

deref and reset do work for me

πŸ‘ 3
djblue22:09:34

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

djblue22:09:55

Thanks for all the hard work!

borkdude22:09:25

It's a nice challenge

borkdude22:09:23

Ah I see what's wrong.

borkdude22:09:31

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

borkdude22:09:18

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

borkdude09:09:23

Should be fixed now

djblue16:09:18

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

borkdude12:09:48

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

πŸŽ‰ 3
jeremys13:09:20

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?

borkdude13:09:41

@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

borkdude13:09:48

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.

borkdude14:09:39

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

borkdude14:09:10

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

borkdude14:09:34

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

borkdude14:09:22

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

borkdude14:09:00

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

jeremys14:09:56

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

borkdude14:09:42

maybe your reader could just return strings instead?

jeremys14:09:32

yes it's a possibility I can explore.

borkdude14:09:34

which you can then evaluate either using clojure or sci

borkdude14:09:01

clojure has load-string as the equivalent api

borkdude14:09:05

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

jeremys14:09:21

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

borkdude14:09:52

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

jeremys14:09:06

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.

borkdude14:09:36

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

borkdude14:09:00

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

borkdude14:09:41

so you could do:

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

borkdude14:09:52

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)

borkdude14:09:57

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

jeremys14:09:20

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.

borkdude14:09:52

that should work yes

jeremys14:09:12

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.})β—Š
   })β—Š

jeremys14:09:34

[(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   "]}))]

borkdude14:09:11

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

jeremys14:09:15

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.

borkdude14:09:06

you could also consider using reader tags

jeremys14:09:21

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

borkdude14:09:49

org-mode also has something like this I think

jeremys14:09:33

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

jeremys14:09:35

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

borkdude14:09:50

yes, please keep me updated

borkdude15:09:38

@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

borkdude15:09:52

Feel free to experiment with this and post feedback

borkdude15:09:59

This is in sci

jeremys19:09:55

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

djblue22:09:13

This is really cool!