babashka

2025-11-01T16:32:30.879229Z

FYI, I started a discussion for an idea on how Babashka could fake the feature of allowing the implementation of well known Clojure/Java interfaces. This would allow to create custom types that work like maps, sets, etc e.g. something like https://github.com/Malabarba/lazy-map-clojure/. I am curious to hear your opinions https://github.com/babashka/babashka/discussions/1884

borkdude 2025-11-01T16:34:33.940019Z

I've thought about this before and in fact IDeref and IAtom work like this. But with other interfaces like ILookup there's the problem that you have to patch core functions that use get and all other functions that use those core functions if they are not loaded from source but pre-compiled

2025-11-01T16:37:16.465529Z

Yeah I think I mentioned that the example, unless you mean something different. I don't know if the performance implications are significant or if there are other problems i didn't think about.

borkdude 2025-11-01T16:37:33.414059Z

If it's possible to patch Clojure's ILookup protocol and somehow hack that, that would be an option. In CLJS this is possible and I've thought about doing this for SCI on JS

borkdude 2025-11-01T16:38:24.345919Z

well you mention that you should patch get, but you should also patch all other functions that use get

2025-11-01T16:39:28.281029Z

Ah there you go, that was the thing i didn't think about yet. Hmm that might be more complicated then

borkdude 2025-11-01T16:39:49.544519Z

unless you can patch clojure's ILookup somehow

2025-11-01T16:40:50.597369Z

Would it be possible to have the ILookup interface call the ILookup protocol? Or maybe that is too low level to work

borkdude 2025-11-01T16:41:05.343229Z

I think here is an example of messing with clojure's ILookup: https://github.com/phronmophobic/scify

borkdude 2025-11-01T16:42:22.879689Z

probably doesn't work for AOT-code, but reloading code should fix that

2025-11-01T16:42:34.844759Z

This is hacking from the outside I guess, but since you control babashka it would be technically possible to change the actual clojure source code if that would help

borkdude 2025-11-01T16:42:41.942999Z

yeah indeed

2025-11-01T16:43:00.972919Z

Not saying it is worth it, but just as an idea

borkdude 2025-11-01T16:43:13.109259Z

it's actually something that might be worth it

2025-11-01T16:44:35.196959Z

It is already amazing how many things I can already do just with Babashka, but for this particular case for a custom type I cannot find a hack that works for me

borkdude 2025-11-01T16:45:00.737629Z

I don't know if @smith.adriane’s hack is going to work for ILookup. When you apply this hack, get is probably just going to use the inlined protocol stuff instead of the hacked var

borkdude 2025-11-01T16:45:12.726319Z

you mean the reitit thing right?

borkdude 2025-11-01T16:46:25.625259Z

I think the reitit thing isn't going to perform well in SCI compared to just doing it the straightforward way like e.g. https://github.com/tonsky/clj-simple-router which works in bb. The more you lean on pre-compiled core functions, the faster it'll get. But I haven't benchmarked anything so I might be wrong

2025-11-01T16:46:36.922959Z

It's related, as I build upon Reitit, but this for a tool like Sinatra (in Ruby). It sort of works now with Babashka, but I would like to have something magic with request params and I can only do it nicely with a custom type i think

borkdude 2025-11-01T16:47:21.269929Z

You can make custom maps like this here: https://blog.wsscode.com/guide-to-custom-map-types/

borkdude 2025-11-01T16:48:33.920789Z

scroll down to CMT in Babashka

2025-11-01T16:48:50.112639Z

Ah cool that might be what i need. Thanks! I'll have a look at and let you know later

borkdude 2025-11-01T17:37:05.267149Z

O wait, I overlooked something. ILookup is of course an interface.

borkdude 2025-11-01T17:37:13.231669Z

not a protocol. So I can't patch it

borkdude 2025-11-01T17:44:15.672589Z

yeah I guess I could fork clojure... :-s

phronmophobic 2025-11-01T17:44:56.477999Z

Yea, I think scify only works for protocols and only when you are building the sci ctx yourself (ie. not babashka compatible).

borkdude 2025-11-01T17:47:12.832509Z

yeah you mean: not from source. that's not expected of course. we were just looking for a hack to make ILookup properly work within SCI in combination with pre-compiled stuff

borkdude 2025-11-01T17:48:01.779149Z

I guess I could still fork clojure and patch get

borkdude 2025-11-01T17:51:18.493049Z

maybe just reloading clojure.core + patching get would get rid of the AOT inlining effect

borkdude 2025-11-01T17:55:52.799559Z

no get has an inline thing. patching clojure.lang.RT/get would be more effective

borkdude 2025-11-01T19:08:50.570689Z

this still wouldn't solve the problem for other Java interfaces though which are common in deftype / reify

2025-11-02T10:12:47.155139Z

Yeah sounds like it is quite tricky. I will think about what you said. I didn't think it through that much before

2025-11-02T10:13:42.073379Z

Btw, the proxy approach works. So that's already nice

πŸŽ‰ 1
Ryan Tate 2025-11-01T03:59:56.937979Z

I am curious what the usual approach is for deploying a babashka script that is part of a project that also has non CLI, web app functionality. I have a project that is essentially a pedestal app. There is a companion command line tool, also in the project, done via babashka script (which uses some project namespaces) at the root project level alongside a bb.edn. This works well locally as a project dir, I can run the script as well as load/launch web app stuff from a repl. But this all ultimately needs to run on the server. To deploy as a web app there I make an uberjar of the project via tools.build and put this on the server and it gets launched and run as a service. Is there some way to run the babshka script from within that project uberjar? Or do I need to do something else to make it available? Maybe I should https://book.babashka.org/#_uberscript separately? Is that what people usually do? Or I could just git clone the project dir onto the server and setup the bb cli path as I do locally. Thanks for any general guidance πŸ™

βœ… 1
mkvlr 2025-11-01T07:46:18.022269Z

if the bb sources are in the jar, you should be able to use them via bb β€”classpath project.jar ,,,

πŸ‘ 1
πŸ™Œ 1
borkdude 2025-11-01T09:19:34.281409Z

I have you have tasks, you can put them in resources/META-INF/bb.edn (so on the classpath in META-INF ), then you can also use them with a jar (without having them on disk)

πŸ‘ 1
Ryan Tate 2025-11-01T17:57:43.905989Z

Thank you both for the info!! Very helpful πŸ™

Ryan Tate 2025-11-01T18:42:20.947619Z

My bb script (and its dependencies) are in the jar but how would I give the path to it? I tried bb -cp project.jar toolname, where toolname is at the root of the project dir, but I get an error "File does not exist." Or do I need to have a copy of the script outside the jar? (Pretty easy, it just invokes one of the fns in the jar) (Going to send this to the main channel for visibilty also but if this is considered rude please tell me πŸ™‚ )

βœ… 1
Ryan Tate 2025-11-01T18:54:46.789599Z

Also when I try to invoke the script alongside the jar, I get a different error, which is odd because it's an sci error I never get when invoking the script locally without the jar (with project dir). Edited for brevity: bb -cp target/myapp-1.0.336-standalone.jar toolname ----- Error -------------------------------------------------------------------- Type: clojure.lang.ExceptionInfo Message: Invalid assignment target Data: {:type :sci/error, :line 1962, :column 3, :file "clojure/spec/alpha.clj", :phase "analysis"} Location: clojure/spec/alpha.clj:1962:3 Phase: analysis ----- Context ------------------------------------------------------------------ 1958: 1959: Initially set to boolean value of clojure.spec.check-asserts 1960: system property. Defaults to false." 1961: [flag] 1962: (set! (. clojure.lang.RT checkSpecAsserts) flag)) ^--- Invalid assignment target [... cut ...] ----- Stack trace -------------------------------------------------------------- clojure.spec.alpha/fn - clojure/spec/alpha.clj:1962:3 [... cut ...] clojure/spec/alpha.clj:1955:1 my.app.cli - my/app/cli.cljc:3:3 my.app.toolname - /path/to/toolname:4:3

borkdude 2025-11-01T18:58:42.760499Z

oh hmm, clojure.spec is in your jar, which is a problem for bb since then it's going to try to load it from source

borkdude 2025-11-01T18:58:51.338079Z

which it can't

Ryan Tate 2025-11-01T18:59:14.456729Z

ah ok - maybe i will make a different jar/project for the cli tool

borkdude 2025-11-01T18:59:22.818279Z

yeah that would probably be best

borkdude 2025-11-01T18:59:35.862829Z

you can do so with bb uberjar myjar.jar -m the-main-fn

Ryan Tate 2025-11-01T18:59:57.436619Z

ok cool

Ryan Tate 2025-11-01T19:00:07.057819Z

thank you!

borkdude 2025-11-01T19:00:37.161719Z

I tried bb -cp project.jar toolname, where toolname is at the root of the project dir, but I get an error "File does not exist."
Are you 100% sure a file exists on the filesystem in that directory named toolname?

Ryan Tate 2025-11-01T19:04:36.901719Z

The file definitely exists at the root of the project dir /that i built the jar from/, but not in the dir where i was typing the bb command, if that makes sense.

borkdude 2025-11-01T19:05:17.116489Z

bb file is never going to load something from a jar file, always file system

Ryan Tate 2025-11-01T19:05:39.412989Z

ah ok

Ryan Tate 2025-11-01T19:05:41.852979Z

my bad

borkdude 2025-11-01T19:05:53.882769Z

if you want to load a namespace from the classpath that can be done using:

bb -e "(require 'my-namespace)"

borkdude 2025-11-01T19:06:50.961379Z

or if you have a function defined like:

(ns myns)
(defn foo [{:keys [x y z]}]
  (do-something))
then you can run it with bb -x myns/foo --x 1 --y 2 --z 3

borkdude 2025-11-01T19:07:53.414049Z

or -main:

(ns myns)
(defn -main [& args]
  (do-something))
bb -m myns

Ryan Tate 2025-11-01T19:10:58.029919Z

Oh those are good ideas to avoid moving the script. Right now the script is just an ns with a script guard -- (when (= *file* (System/getProperty "babashka.file"))) -- but I could define an fn in there with the same contents as what is inside the guard

borkdude 2025-11-01T19:12:12.489919Z

yep

borkdude 2025-11-01T19:12:25.248899Z

that's a common pattern. define a function and call it in the guard

βœ… 1