babashka

https://github.com/babashka/babashka. Also see #sci, #nbb and #babashka-circleci-builds .
djblue 2020-09-05T07:36:54.106100Z

With echo '(defrecord Example [] clojure.lang.IDeref (deref [this]))' | bb I get java.lang.ClassCastException: java.lang.Class cannot be cast to java.util.concurrent.Future . Not sure what's happening :thinking_face:

borkdude 2020-09-05T07:39:10.106900Z

@djblue There are two problems here: - bb doesn't execute programs from stdin, it rather reads EDN, etc from stdin - sci doesn't support implementing interfaces (yet), only protocols

👌 1
djblue 2020-09-05T07:40:28.107800Z

How hard would supporting interfaces be?

borkdude 2020-09-05T07:41:17.108700Z

protocols in sci are just syntactic sugar over multi-methods, so there isn't really an interface class when you define a protocol. the problem with interfaces is that in normal Clojure it creates a real Java class which implements that interface, but in GraalVM you can't create new classes at runtime

👌 1
borkdude 2020-09-05T07:42:36.109700Z

as a workaround I'm considering something like pre-defined recipes to reify a selection of classes, but I haven't spent a lot of time on that yet. it's more or less a sci research topic

1
borkdude 2020-09-05T07:44:27.111300Z

e.g. mapping a function like: (defn foo [opts] (reify IDeref ....) probably works, so then we have a function at compile time that can create an IDeref given some opts. But how do we get a function that can reify more than one interface?

djblue 2020-09-05T07:46:31.112200Z

I wonder if maybe we can proxy the interfaces via protocols?

borkdude 2020-09-05T07:46:50.112400Z

hmm, interesting

borkdude 2020-09-05T07:47:57.113600Z

I think for IDeref that would work, if you call deref yourself in sci, since we can patch deref to go through the "protocol" instead of the interface, but if you pass that object to other functions that use clojure's deref, that it will break

borkdude 2020-09-05T07:49:21.114600Z

that's also the problem with implementing Java interfaces: we can fake things in sci, but as soon as you pass the object to some other Java class, that class doesn't know we are faking things

djblue 2020-09-05T07:55:06.116300Z

Thanks for all the context, I'll keeping thinking about this problem.

borkdude 2020-09-05T07:55:53.117100Z

Thanks, yeah, it's really one of the more challenging problems in sci. Since protocols are only used from Clojure they are more manageable, although that was also a bit of a head-scratcher :)

borkdude 2020-09-05T07:57:11.117800Z

Protocols can now even be implemented via metadata, which is so much more flexible than having to create classes

💯 1
borkdude 2020-09-05T20:01:02.120Z

@djblue Were you particularly interested in implementing IDeref or was the question a general question? Since IDeref is Clojure only, I think we could get away with re-implementing that as a protocol in bb/sci

djblue 2020-09-09T06:18:25.136700Z

I got the re-routing through another fn working, but I'm not sure how to register the new protocol

djblue 2020-09-09T06:18:34.136900Z

https://github.com/djblue/sci/commit/c84e708e794d9e6c877b0addada3d0bb801eb202 is my current progress

borkdude 2020-09-09T07:19:20.137100Z

Take a look at babashka.impl.protocols

borkdude 2020-09-09T07:20:13.137400Z

There we define a multimethod for each protocol method:

(defmulti datafy types/type-impl)

borkdude 2020-09-09T07:20:59.137600Z

Then we dispatch on some key which indicates this is a "reified" thing:

(defmethod datafy :sci.impl.protocols/reified [x]
  (let [methods (types/getMethods x)]
    ((get methods 'datafy) x)))

(defmethod datafy :default [x]
  ;; note: Clojure itself will handle checking metadata for impls
  (d/datafy x))

borkdude 2020-09-09T07:21:17.137800Z

And for all other things we defer to the original datafy

djblue 2020-09-09T07:21:51.138Z

How we we get sci to resolve the new protocol?

borkdude 2020-09-09T07:22:24.138200Z

Look at the bottom of that file. There we insert "vars" into the namespaces

borkdude 2020-09-09T07:23:25.138400Z

so then it will call our multimethod instead of the core var

borkdude 2020-09-09T07:24:13.138600Z

so we have to insert our deref multimethod into clojure.core of sci

djblue 2020-09-09T07:24:48.138800Z

So I have that part working, it sees the deref method

borkdude 2020-09-09T07:24:58.139Z

the protocol IDeref itself is also just a var that points to a map with methods

djblue 2020-09-09T07:25:10.139200Z

Where should that be registered?

djblue 2020-09-09T07:25:33.139400Z

sci.impl.namespaces?

borkdude 2020-09-09T07:25:38.139600Z

hmm I see your point

borkdude 2020-09-09T07:25:59.139800Z

clojure.lang.IDeref is the class name right?

djblue 2020-09-09T07:26:23.140Z

Yes, the interface, not sure if that matters :thinking_face:

borkdude 2020-09-09T07:27:05.140200Z

I think I also patched import so it can import protocols (which also generate interfaces in Clojure)

borkdude 2020-09-09T07:27:20.140400Z

I'd have to take a look

djblue 2020-09-09T07:27:34.140600Z

I'll see how import works

djblue 2020-09-09T07:27:48.141Z

Thanks!

borkdude 2020-09-09T07:29:08.141600Z

I don't know if we already covered the case of importing an interface which is implemented as a protocol/multimethod in sci

borkdude 2020-09-09T07:29:24.141800Z

but if you get stuck on this, I'd be happy to take a stab at it

djblue 2020-09-09T07:31:06.142Z

Well I'm done looking into it today, might have time to continue tomorrow. I wouldn't be sad if you figured it out before I did 😄

borkdude 2020-09-09T07:31:52.142200Z

I might

💯 1
borkdude 2020-09-09T07:33:45.142500Z

ah, we have something called resolve-record-class which is used to determine if a class represents some defrecord that sci created. we try that, if the class can't otherwise be resolved

borkdude 2020-09-09T07:34:13.142700Z

maybe we can have something like resolve-protocol-class as well, as a fallback to normal classes. I'll think about it some more

👍 1
borkdude 2020-09-09T08:24:48.143Z

can you PR your existing work to the IDeref branch of sci?

👍 1
borkdude 2020-09-10T08:32:55.153500Z

I probably won't get to this until the weekend

👌 1
djblue 2020-09-05T20:02:38.120100Z

IDeref and IAtom

djblue 2020-09-05T20:02:58.120300Z

I was going to refiy them and wire them into portal 👌

djblue 2020-09-05T20:04:32.120500Z

@portal will give you the current value in portal and swap! and reset! will let you update it

djblue 2020-09-05T20:04:59.120700Z

repl -> portal -> repl ...

borkdude 2020-09-05T20:06:21.120900Z

why not use a normal atom and add-watch?

borkdude 2020-09-05T20:07:01.121100Z

or some functions around the state

djblue 2020-09-05T20:08:50.121400Z

The state isn't in the user's runtime, it's in the client runtime. So it isn't a real atom 😅

borkdude 2020-09-05T20:09:35.121600Z

Right, but you can write a client API for this right? api/view, api/swap! etc

djblue 2020-09-05T20:10:40.121800Z

True, but clojure.core/swap! is available everywhere in the same way clojure.core/tap>

djblue 2020-09-05T20:11:04.122Z

This is not a technical limitation, just a developer experience experiment

djblue 2020-09-05T20:12:36.122200Z

When I'm debugging I tend to be focused on my problem and I reach for what's easy, mostly prn and tap>

borkdude 2020-09-05T20:13:06.122400Z

ok. it would be interesting if we could make this work. an overview of functions in Clojure that use deref and the swap/reset should be made, but I think they are mostly used directly?

borkdude 2020-09-05T20:13:46.122600Z

they should then be re-routed through the protocol

borkdude 2020-09-05T20:14:21.122800Z

similar to what was done for Datafiable etc

👌 1
djblue 2020-09-05T20:15:09.123100Z

That solution makes sense

djblue 2020-09-05T20:15:50.123300Z

What's a good place to start looking at how to do this?

borkdude 2020-09-05T20:17:02.123500Z

let's start with just deref. A protocol should be inserted named IDeref, in the same manner how we did Datafiable / Navigable. They are implemented as a multimethod under the hood (because that doesn't require compilation and we can't do compilation in GraalVM).

👍 1
borkdude 2020-09-05T20:17:17.123700Z

so you can look up the commit for those in sci and see how that was done

djblue 2020-09-05T20:17:57.124Z

Ok, thanks for the info!

borkdude 2020-09-05T20:18:04.124200Z

next, we have to patch deref with a function that checks if the object implements our thing, if so, then we call our multimethod, if not, we call normal clojure deref

👍 1