babashka

pieterbreed 2025-04-19T09:14:18.208479Z

I am trying to use the potetm/fusebox library with babashka and getting this error: clojure.lang.ExceptionInfo: Method name on class java.lang.ThreadBuilders$VirtualThreadBuilder not allowed! Is this something I am doing wrong or behaviour I can change? I understand that fusebox has been made to work with clojurescript so it must be "portable" on some level at least. Is there anything I can do to help make this work on bb? Alternatively, is there perhaps a suggestion of another (fault-tolerance) library that is known to work with bb?

❀️ 1
βœ… 2
2025-04-22T12:35:16.318149Z

@pieterbreed Oh I meant to say this the other day: Fusebox works with cljs because there's a completely different implementation for it. It's very much tied to the host VM.

πŸ‘πŸ½ 1
pieterbreed 2025-04-22T12:35:45.985759Z

From the conversation I figured as much πŸ™‚

πŸ‘ 1
borkdude 2025-04-22T12:38:14.447339Z

I have a working fix in SCI/bb that now actually uses type hints when doing the interop (thus you get 3 instead of nil consistently), but I also made one breaking change: when providing a type hint that can't be resolved, it's an error (like in Clojure). Now I ran into a failure with the meander library which is tested in CI.

275:                :counter/value ?value
276:                :as ?element}
277:               ^& ?rest-data}}]
278:     (let [element* (update ?element :counter/value inc)
279:           data* (conj ?rest-data element*)
                     ^--- Unable to resolve classname: &
280:           env* (assoc env :data data*)]
281:       [`(nth ~?symbol ~?value nil) env*])
I need to investigate what meander is doing here and why there is a ^& symbol and how this ends up in the let* form Maybe I shouldn't let bb/SCI break on invalid type hints, still contemplating this.

2025-04-22T12:39:39.618209Z

what would ^& even do? is it parsed as just a symbol? does that work in clojure?

borkdude 2025-04-22T12:40:29.642299Z

you can attach any tag to data, but SCI may be wrong about preserving this tag somewhere in a macroexpansion or so https://github.com/noprompt/meander/blob/74de6b1f651441092cc12d1c9012ef7086033040/src/meander/substitute/epsilon.cljc#L425

borkdude 2025-04-22T12:41:19.354189Z

no time for this today, hopefully will find out tomorrow

πŸ‘ 1
borkdude 2025-04-20T18:45:50.507639Z

Some more debugging done, if the native image fails it seems to be picking a different method overload than when it succeeds:

Native failing:

ctxClass: class java.util.concurrent.ThreadPoolExecutor
meth: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.lang.Runnable)
boxedArgs [Ljava.lang.Object;@7d8d4a71
retTYpe: class java.lang.Object

Native working:

ctxClass: class java.util.concurrent.ThreadPoolExecutor
meth: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
boxedArgs [Ljava.lang.Object;@3729817d
retTYpe: class java.lang.Object
This is when calling .submit in (.submit ^ExecutorService (Executors/newCachedThreadPool) f) I can't explain the randomness but at least found the difference

1
borkdude 2025-04-20T20:21:24.200499Z

It seems the reflector (which bb + SCI uses to implement interop) tries the methods in different order for the failing native image: Failing:

Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.lang.Runnable)
Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
found method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.lang.Runnable)
Working:
Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.lang.Runnable)
found method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
This is inside matchMethod

borkdude 2025-04-20T20:22:26.179129Z

Research to be continued

borkdude 2025-04-20T21:03:51.387249Z

More info:

user=> (.isAssignableFrom Runnable Callable)
false
user=> (.isAssignableFrom Callable Runnable)
false
Here's the problem. The Reflector can't decide which method is better

borkdude 2025-04-20T21:04:17.824189Z

so it just takes the first one which fits. but if it takes the Runnable method, then the result from .get will be nil

2025-04-20T21:12:03.057679Z

Would it be fixed if I type hint it?

2025-04-20T21:12:25.382499Z

I didn’t intend to have any reflection at all.

borkdude 2025-04-20T21:12:42.440189Z

it's already type hinted, but since the reflector doesn't use type hints this won't help. it's not your problem, it's something I have to think about fixing in bb/SCI

borkdude 2025-04-20T21:13:07.987869Z

bb / SCI always uses reflection to implement interop since it's an interpreter, not a compiler

πŸ‘ 1
borkdude 2025-04-20T21:15:55.594129Z

I think I'll just have to make a more sophisticated reflector which passes the information from the type hints along

borkdude 2025-04-20T21:16:17.682189Z

instead of deriving them from the concrete arguments

borkdude 2025-04-20T21:16:30.425699Z

which is hopefully doable!

borkdude 2025-04-20T21:22:06.950019Z

made this issue: https://github.com/babashka/sci/issues/959

borkdude 2025-04-19T09:15:33.948749Z

yes of course, we could fix this

borkdude 2025-04-19T09:15:45.484109Z

let me check

πŸ™πŸ½ 1
borkdude 2025-04-19T09:54:13.368819Z

I have this example working now:

(require '[com.potetm.fusebox.bulkhead :as bh])

(def bulkhead
  (bh/init {::bh/concurrency 2
            ::bh/wait-timeout-ms 100}))

(defn run []
  (prn :dude))

(bh/with-bulkhead bulkhead
  (run))

pieterbreed 2025-04-19T09:54:38.035889Z

awesome! Mine broke already on the require πŸ™‚

borkdude 2025-04-19T09:54:57.296759Z

yeah, mine too, but a small fix made everything work :)

borkdude 2025-04-19T09:54:59.521269Z

PR incoming

pieterbreed 2025-04-19T09:55:04.584229Z

Thank you πŸ™πŸ½

borkdude 2025-04-19T09:57:01.026579Z

once I've merged https://github.com/babashka/babashka/pull/1810 and master CI finishes you can run bb with:

bash <(curl )  --dev-build --dir /tmp

borkdude 2025-04-19T10:13:23.872079Z

merged it. will take a few minutes before master finishes but then you'll be able to use it. which OS are you on?

pieterbreed 2025-04-19T10:13:35.649769Z

linux

πŸ‘ 1
borkdude 2025-04-19T10:14:26.522679Z

ok, please test the --dev-build once CI finishes and if you need a proper other than the dev-build release let me know

pieterbreed 2025-04-19T10:14:55.914539Z

ok, I'm about to step out, will let you know over the weekend if that's ok

borkdude 2025-04-19T10:15:05.218619Z

sure, no hurry

pieterbreed 2025-04-19T10:57:56.283059Z

ok, gave it a quick whirl. I think there are more classes missing: The basic all-in-one test is this:

(require '[com.potetm.fusebox.bulkhead :as bh]
         '[com.potetm.fusebox.bulwark :as bw]
         '[com.potetm.fusebox.circuit-breaker :as cb]
         '[com.potetm.fusebox.fallback :as fallback]
         '[com.potetm.fusebox.rate-limit :as rl]
         '[com.potetm.fusebox.retry :as retry]
         '[com.potetm.fusebox.timeout :as to])
Below, I've done the ones that fail individually so you can see all the errors I've seen so far:
user> (require '[com.potetm.fusebox.retry :as retry])
java.lang.Exception: Unable to resolve classname: java.util.concurrent.ThreadLocalRandom user com/potetm/fusebox/retry.clj:8:3

user> (require 'com.potetm.fusebox.circuit-breaker)
java.lang.Exception: Unable to resolve classname: java.util.concurrent.locks.ReentrantLock user com/potetm/fusebox/circuit_breaker.clj:7:3

user> (require 'com.potetm.fusebox.memoize)
java.lang.Exception: Unable to resolve classname: java.util.concurrent.ConcurrentHashMap user com/potetm/fusebox/memoize.clj:5:3

pieterbreed 2025-04-19T10:58:38.146539Z

I'm wondering if there's a more direct way to see all the classes that the lib wants to load... or would that be looking at the source code?

borkdude 2025-04-19T11:21:54.948269Z

yeah, I'll take a look at fixing these too

borkdude 2025-04-19T11:41:57.728579Z

pushed fixes to main now, wait for CI to finish and then try again

borkdude 2025-04-19T11:52:22.552279Z

I also added ConcurrentHashMap for the memoize namespace

borkdude 2025-04-19T11:52:37.007719Z

After that I ran the test suite:

Ran 9 tests containing 182 assertions.
1 failures, 1 errors.

borkdude 2025-04-19T11:53:01.339479Z

one test is failing in the bulwark namespace and another one in the timeout namespace

borkdude 2025-04-19T12:06:35.765889Z

Ah when fixing something else I got all the tests working

borkdude 2025-04-19T12:07:14.477309Z

but this requires a little change in fusebox (replacing some low level thing with a thing that's already in clojure: bound-fn*)

2025-04-19T12:22:38.809959Z

Looking at this as well. I should be able to push a patch for it later today.

pieterbreed 2025-04-19T12:24:15.125389Z

Wow. Thank you. πŸ™πŸ½

borkdude 2025-04-19T12:25:34.799929Z

With some changes I discussed with @potetm all works on the JVM, but when compiled native I'm still getting:

FAIL in (bulwark-test) (/Users/borkdude/dev/fusebox/test/com/potetm/fusebox/bulwark_test.clj:12)
it works
expected: (= [:something :dangerous] (bw/bulwark spec (Thread/sleep 100) [:something :dangerous]))
  actual: (not (= [:something :dangerous] nil))

borkdude 2025-04-19T12:25:52.289059Z

Could it be there is some exception being suppressed?

borkdude 2025-04-19T12:26:07.803739Z

I bet it has to do with some reflection issue that can be solved but I'm only seeing nil :)

borkdude 2025-04-19T12:26:38.885849Z

(bw/bulwark nil #_ spec
               (Thread/sleep 100)
               [:something :dangerous])
this does work πŸ˜†

2025-04-19T12:26:54.045049Z

right that goes into the fallthrough

2025-04-19T12:27:59.966209Z

I would expect either an exception or the value or :yes! from the fallback

2025-04-19T12:29:39.284539Z

maybe inline the bh/bulwark call and start commenting out specific utilities?

2025-04-19T12:29:58.240679Z

`(fallback/with-fallback ~spec
     (retry/with-retry ~spec
       (cb/with-circuit-breaker ~spec
         (bh/with-bulkhead ~spec
           (rl/with-rate-limit ~spec
             (to/with-timeout ~spec
               ~@body))))))

borkdude 2025-04-19T12:30:12.659909Z

I've got a repro here:

(require '[com.potetm.fusebox.bulkhead :as bh]
         '[com.potetm.fusebox.bulwark :as bw]
         '[com.potetm.fusebox.circuit-breaker :as cb]
         '[com.potetm.fusebox.fallback :as fallback]
         '[com.potetm.fusebox.rate-limit :as rl]
         '[com.potetm.fusebox.retry :as retry]
         '[com.potetm.fusebox.timeout :as to])

(def spec (merge (retry/init {::retry/retry? (fn [c dur ex]
                                                    (prn :dur dur :ex ex)
                                                    (< c 10))
                                   ::retry/delay (constantly 10)})
                      (to/init {::to/timeout-ms 500})
                      (fallback/init {::fallback/fallback (fn [ex]
                                                            (prn :ex ex)
                                                            :yes!)})
                      (cb/init {::cb/next-state (partial cb/next-state:default
                                                         {:fail-pct 0.5
                                                          :slow-pct 0.5
                                                          :wait-for-count 3
                                                          :open->half-open-after-ms 100})
                                ::cb/hist-size 10
                                ::cb/half-open-tries 3
                                ::cb/slow-call-ms 100})
                      (rl/init {::rl/bucket-size 10
                                ::rl/period-ms 1000
                                ::rl/wait-timeout-ms 100})
                      (bh/init {::bh/concurrency 5
                                ::bh/wait-timeout-ms 100})))

(prn (bw/bulwark spec (Thread/sleep 100) [:works]))
Yes, I'll do that

borkdude 2025-04-19T12:32:15.177029Z

This also returns nil:

(prn (to/with-timeout spec
       (Thread/sleep 100) [:works]))

borkdude 2025-04-19T12:33:57.231549Z

When I uncomment:

(to/init {::to/timeout-ms 500})
it does work...

2025-04-19T12:34:54.586419Z

wat

borkdude 2025-04-19T12:35:23.482499Z

I'll add some debugging in the timeout logic

2025-04-19T12:36:18.059519Z

init only adds the ::interrupt true, which is only used if it's gonna throw

2025-04-19T12:37:29.419099Z

but the timeout test suite passes?

borkdude 2025-04-19T12:37:36.383419Z

Aha!

(.get fut
                     to
                     TimeUnit/MILLISECONDS)
This returns nil in bb but does work on the JVM... So I've got a decent repro now. Yes, that passes

2
2025-04-19T12:39:10.132869Z

huh, I'll probably add a test to the test suite for this then

2025-04-19T12:39:16.353719Z

(don't tell twitter)

borkdude 2025-04-19T12:39:37.271489Z

it's not your fault, it's a bug in bb

2025-04-19T12:40:27.837979Z

no dont tell them I added a test. they'll get the wrong idea.

borkdude 2025-04-19T12:40:33.919619Z

hehe sure

2025-04-19T12:40:44.928579Z

(kidding)

borkdude 2025-04-19T12:47:18.628109Z

of course ;) This does print the correct result in bb native:

(import '(java.util.concurrent FutureTask Executors))

;; Create a callable (a function that returns a value)
(defn my-task []
  (println "Running in a virtual thread!")
  (Thread/sleep 10)
  "Task result")

;; Wrap the callable in a FutureTask
(def future-task (FutureTask. ^Callable (reify java.util.concurrent.Callable
                                          (call [_] (my-task)))))

;; Create a virtual thread per task executor
(def executor (. Executors newVirtualThreadPerTaskExecutor))

;; Submit the FutureTask to the executor (this starts it)
(.submit executor future-task)

;; You can now block to get the result
(prn "Result:" (.get future-task 100 java.util.concurrent.TimeUnit/MILLISECONDS))
So it doesn't seem to be an interop thing on FutureTask. Perhaps the body of the callable returns nil somehow in bb native

borkdude 2025-04-19T12:48:27.595409Z

that would be weird as well.

2025-04-19T12:50:33.908759Z

does putting a delay around the executor make a difference?

2025-04-19T12:51:16.637749Z

also, I wonder what using a native threadpool does

borkdude 2025-04-19T12:52:23.112819Z

dereferencing the futuretask with (deref fut to ::o=noes) also returns nil

borkdude 2025-04-19T12:55:59.301079Z

got something here, when I replace the timeout executor with:

(defonce ^:private
  timeout-threadpool
  (delay (. Executors newVirtualThreadPerTaskExecutor) #_(Executors/newCachedThreadPool (let [tc (AtomicLong. -1)]
                                          (reify ThreadFactory
                                            (newThread [this r]
                                              (doto (Thread. r)
                                                #_(.setName (str "fusebox-thread-"
                                                               (.incrementAndGet tc)))
                                                #_(.setDaemon true))))))))
it starts to work again, so it seems to be something with the cachedThreadPool

2025-04-19T12:59:43.355749Z

try Executors/newSingleThreadExecutor ?

2025-04-19T13:00:19.161199Z

I gotta run to coach a soccer game. I'll be back later in the day to push my fixes for this.

borkdude 2025-04-19T13:00:29.094269Z

sure, enjoy!

borkdude 2025-04-19T13:00:32.020759Z

and thanks

borkdude 2025-04-19T13:01:34.874299Z

both:

- (. Executors newVirtualThreadPerTaskExecutor)
- (Executors/newSingleThreadExecutor)
work instead of the cached one. but it could be a bug with reify or something, which is a complicated thing bb. I'll investigate further

borkdude 2025-04-19T13:04:44.021439Z

Hmm, this whole thing does the right thing too:

(import '(java.util.concurrent FutureTask Executors))

;; Create a callable (a function that returns a value)
(defn my-task []
  (Thread/sleep 10)
  "Task result")

;; Wrap the callable in a FutureTask
(def future-task (FutureTask. ^Callable my-task))

;; Create a virtual thread per task executor
(def executor (Executors/newCachedThreadPool
               (reify java.util.concurrent.ThreadFactory
                 (newThread [_ r]
                   (let [tc (java.util.concurrent.atomic.AtomicLong. -1)]
                     (doto (Thread. r)
                       (.setName (str "fusebox-thread-"
                                      (.incrementAndGet tc)))
                       (.setDaemon true)))))))

;; Submit the FutureTask to the executor (this starts it)
(.submit executor future-task)

;; You can now block to get the result
(prn "Result:" (.get future-task 100 java.util.concurrent.TimeUnit/MILLISECONDS))

;; Shutdown the executor afterwards
(.shutdown executor)

borkdude 2025-04-19T13:05:18.472789Z

in bb native. I'll have to park this for the moment as well, I'll continue later

borkdude 2025-04-19T13:15:17.685169Z

Got a small repro here now:

(import '(java.util.concurrent Executors ThreadFactory ExecutorService))

(defonce ^:private
  timeout-threadpool
  (delay (. Executors newVirtualThreadPerTaskExecutor)
         (Executors/newSingleThreadExecutor)
         (Executors/newCachedThreadPool
          (reify ThreadFactory
            (newThread [_this r]
              (Thread. r))))))

(defn timeout* [{to ::timeout-ms} f]
  (let [fut (.submit ^ExecutorService @timeout-threadpool
                     ^Callable f)
        v (.get fut
                to
                java.util.concurrent.TimeUnit/MILLISECONDS)]
    (prn :v v ) ;; nil ;;;
    v))

(prn (timeout* {::timeout-ms 100} (fn [] 3)))

borkdude 2025-04-19T13:22:23.344179Z

Smaller repro:

(import '(java.util.concurrent Executors ExecutorService))

(prn
 (let [fut (.submit ^ExecutorService (Executors/newCachedThreadPool)
                    ^Callable (fn [] 3))]
   (.get fut)))

borkdude 2025-04-19T13:25:44.304339Z

At least I nailed it down to 4 lines now...

borkdude 2025-04-19T15:06:04.462039Z

well, whaddayouknow, I recompiled bb and it works..

Ran 9 tests containing 197 assertions.
0 failures, 0 errors.

borkdude 2025-04-19T15:08:35.160779Z

@potetm Here's my patch for fusebox with a test runner for bb as well. You can invoke the test runner with bb test:bb

borkdude 2025-04-19T15:09:39.140399Z

if you want to apply this patch, I could also add a test run in the Github workflow once bb with the fixes is released

borkdude 2025-04-19T15:18:33.397799Z

to be sure, I added this test to the bb test suite:

(deftest cached-thread-pool
  (is (= 3 (bb nil "(import '(java.util.concurrent Executors ExecutorService))
                    (let [fut (.submit ^ExecutorService (Executors/newCachedThreadPool)
                       ^Callable (fn [] 3))]
   (.get fut))"))))

borkdude 2025-04-19T15:26:19.476009Z

Seems to work on CI. Not sure what was the weird hiccup

borkdude 2025-04-19T15:31:58.679259Z

So steps to finish this up: β€’ apply patch to fusebox β€’ I'll add fusebox tests (unchanged) to bb CI (I test a boatload of libs in bb CI on every commit) β€’ perhaps add the bb test runner to Github actions on fusebox if you're up for it (you can always bug me with problems or just turn it off if you find it gets in the way)

2025-04-19T16:35:28.681329Z

pushed to github

πŸŽ‰ 2
2025-04-19T16:35:58.145919Z

I'll let you add a bb test runner, and after that's working, I'll carve a new release.

borkdude 2025-04-19T18:36:33.001549Z

On Windows I see this failure:

Testing com.potetm.fusebox.timeout-test
FAIL in (timeout-test) (/C:/Users/runneradmin/.gitlibs/libs/com.potetm/fusebox/b61df08b11e4960b5dea2ccfb4532a717c71cb2a/test/com/potetm/fusebox/timeout_test.clj:16)
base case - no sleeping
expected: (< t 15)
  actual: (not (< 15 15))
FAIL in (timeout-test) (/C:/Users/runneradmin/.gitlibs/libs/com.potetm/fusebox/b61df08b11e4960b5dea2ccfb4532a717c71cb2a/test/com/potetm/fusebox/timeout_test.clj:16)
no interrupt
expected: (< t 15)
  actual: (not (< 15 15))
but could just be a flaky test right?

2025-04-19T18:40:38.033039Z

lol ugh, yes, all of timeout is flaky

2025-04-19T18:43:50.074609Z

just pushed an updated to increase the timeout times and tolerances for those tests

borkdude 2025-04-19T21:19:08.702829Z

I'm still running into some of these in other CI (mac) now:

FAIL in (timeout-test) (/Users/distiller/.gitlibs/libs/com.potetm/fusebox/2f42391868c82c193628bec8922f8735ae3cac66/test/com/potetm/fusebox/timeout_test.clj:16)
base case
expected: (< t 25)
  actual: (not (< 58 25))
I guess bb is a bit too slow perhaps ;)

borkdude 2025-04-19T21:19:21.674199Z

I can just skip testing the timeout namespace for now

borkdude 2025-04-19T21:43:51.785259Z

I still randomly get the nil result now and then on CI:

FAIL in (cached-thread-pool) (interop_test.clj:247)
expected: (= 3 (bb nil "(import '(java.util.concurrent Executors ExecutorService))\n                    (let [fut (.submit ^ExecutorService (Executors/newCachedThreadPool)\n                       ^Callable (fn [] 3))]\n   (.get fut))"))
  actual: (not (= 3 nil))
I'd rather have a test that would consistently fail than this one...

2025-04-19T22:20:00.264479Z

wild

borkdude 2025-04-23T11:19:53.575249Z

I'm debugging. I see that meander produces something like this:

(clojure.core/let [debug (quote {:tag &}) ?rest-data T3__3911] nil)))
I inserted the debug binding to show the metadata of the ?rest-data symbol

borkdude 2025-04-23T11:20:14.813429Z

but clojure doesn't stumble over this somehow. it does when I try to write a similar macro..

borkdude 2025-04-23T11:24:56.114489Z

user=> (defn my-let-body [x] `(let [~(:symbol x) ~(:value x)] (inc ~(:symbol x))))
#'user/my-let-body
user=> (defmacro foo [] (my-let-body {:symbol 'x :value 3}))
#'user/foo
user=> (foo)
4
user=> (defmacro foo [] (my-let-body {:symbol (with-meta 'x {:tag '&}) :value 3}))
#'user/foo
user=> (foo)
Syntax error (UnsupportedOperationException) compiling let* at (REPL:1:1).
Can't type hint a local with a primitive initializer

borkdude 2025-04-23T11:26:50.524559Z

Unfortunately I'm no meander expert..

borkdude 2025-04-23T11:30:12.187509Z

This is really interesting. When I override the tag myself like this, it does crash:

~(with-meta (:symbol ir) {:tag '&})

πŸ˜… 1
borkdude 2025-04-23T11:32:53.785479Z

debug2 (quote [& clojure.lang.Symbol] this is what the tag is. it is a symbol & but this one doesn't cause problems....?

borkdude 2025-04-23T11:37:08.783839Z

So this works in clj:

~(vary-meta (:symbol ir) assoc :tag (:tag (meta (:symbol ir))))
but this doesn't:
~(vary-meta (:symbol ir) assoc :tag '&)
weeeird

borkdude 2025-04-23T11:58:36.255209Z

I found that clojure does accept this:

user=> (let* [y nil ^& x y])
nil

2025-04-23T11:59:20.715359Z

yeah but it doesn't actually attach the metadata in that case

borkdude 2025-04-23T11:59:35.548949Z

So this also works:

user=> (defmacro dude [] `(let [y# nil ~(with-meta (symbol "x") {:tag '&})  y#])
)
#'user/dude
user=> (dude)

2025-04-23T11:59:41.624319Z

(let* [y nil ^& x y]
  (meta x))
=> nil

borkdude 2025-04-23T11:59:42.116919Z

What's the rule here?

borkdude 2025-04-23T12:00:13.964299Z

(meta x) is runtime metadata of x, this isn't about type hints

2025-04-23T12:01:47.364349Z

where would you expect the metadata to show up then?

borkdude 2025-04-23T12:02:38.568039Z

I am talking about the tag of x. The metadata doesn't show up unless you inspect it (the binding symbol) in a macro which is what I'm doing in meander right now since it generates this let in a macro.

borkdude 2025-04-23T12:02:53.868279Z

user=> (let [^String x (identity "dude") ^& y x] (.length y))
Syntax error (IllegalArgumentException) compiling . at (REPL:1:43).
Unable to resolve classname: &

borkdude 2025-04-21T15:29:50.038769Z

Looks like I have a fix (which is yet to be optimized but looks like it works):

$ bb /tmp/repro.clj
Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.lang.Runnable)
param classes: interface java.lang.Runnable
isCongruent: false
typesMatch: 0
Trying method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
param classes: interface java.util.concurrent.Callable
isCongruent: true
typesMatch: 0
found method: public java.util.concurrent.Future java.util.concurrent.AbstractExecutorService.submit(java.util.concurrent.Callable)
3 3 3 nil

1
M 2025-04-19T15:57:07.656159Z

I'm struggling with shelling out some more complex commands.

(require '[babashka.process :refer [shell process exec])

(def cmd
  "git branch --sort=-committerdate | while read branch; do if [[ $branch != *\"master\"* ]] && [[ \"$(git log -1 --since='180 days ago' -s $branch)\" == \"\" ]]; then echo \"---\" $branch; fi; done")

(shell cmd)
I get this error
error: unknown option `-'
usage: git branch [<options>] [-r | -a] [--merged] [--no-merged]
   or: git branch [<options>] [-f] [--recurse-submodules] <branch-name> [<start-point>]
   or: git branch [<options>] [-l] [<pattern>...]
   ...
Any ideas?

borkdude 2025-04-19T15:58:24.172539Z

This isn’t a command, it’s a bash program so you should execute it with bash

M 2025-04-19T15:58:42.882859Z

Ahh

borkdude 2025-04-19T15:58:50.253139Z

AFK but will provide an example later

M 2025-04-19T15:59:59.321399Z

This is ringing a bell. bash -c?

borkdude 2025-04-19T16:01:29.461429Z

Yep that’s it

borkdude 2025-04-19T16:01:52.140849Z

Use that as a the first string and then your program as the second

M 2025-04-19T16:02:10.464869Z

Got it. Thanks!