This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-11-07
Channels
- # announcements (11)
- # babashka (29)
- # beginners (70)
- # biff (12)
- # calva (1)
- # clojure (24)
- # clojure-europe (125)
- # clojure-nl (1)
- # clojure-norway (7)
- # clojure-portugal (2)
- # clojure-uk (3)
- # clojurescript (9)
- # core-async (29)
- # cursive (4)
- # emacs (10)
- # etaoin (14)
- # events (3)
- # fulcro (10)
- # funcool (4)
- # helix (1)
- # honeysql (12)
- # introduce-yourself (1)
- # jobs (2)
- # juxt (2)
- # lsp (1)
- # off-topic (17)
- # polylith (58)
- # portal (20)
- # remote-jobs (2)
- # shadow-cljs (2)
- # squint (4)
- # tools-deps (9)
Hi all - I’m having trouble getting this script to run in babashka, when in a future:
#!/usr/bin/env bb
(ns test2)
(require '[com.grzm.awyeah.client.api :as aws]
'[com.grzm.awyeah.credentials :as credentials])
(defn get-tasks
[& {:keys [profile], :or {profile "default"}}]
(aws/invoke (aws/client
{:api 'dms,
:credentials-provider (credentials/profile-credentials-provider "default")})
{:op :DescribeReplicationTasks}))
(defn main []
(print @(future (get-tasks :profile "default"))))
;; (print (get-tasks :profile "default")))
(main)
This runs fine outside a future and it also runs fine in a future - in Clojure without babashka. I’m not sure if there is another step I need to take here, or how to interpret the error. The response I get is:
----- Error --------------------------------------------------------------------
Type: java.util.concurrent.ExecutionException
Message: java.lang.IllegalStateException: Can't set!: clojure.core/*warn-on-reflection* from non-binding thread
Location: /private/tmp/t/src/./test2.bb:16:10
----- Context ------------------------------------------------------------------
12: :credentials-provider (credentials/profile-credentials-provider "default")})
13: {:op :DescribeReplicationTasks}))
14:
15: (defn main []
16: (print @(future (get-tasks :profile "default"))))
^--- java.lang.IllegalStateException: Can't set!: clojure.core/*warn-on-reflection* from non-binding thread
17: ;; (print (get-tasks :profile "default")))
18:
19: (main)
Lastly, and strangest of all (to me): If I simply add an additional declaration into the file, it runs in a future, in babashka just fine:
(def not-used (aws/client
{:api 'dms,
:credentials-provider (credentials/profile-credentials-provider "default")}))
@U0250GGJGAE Aha! I might have an idea
With Clojure you get the same thing:
$ clj -M -e '@(future (set! *warn-on-reflection* true))'
Execution error (IllegalStateException) at user/eval1$fn (REPL:1).
Can't set!: *warn-on-reflection* from non-binding thread
My suspicion is that there is a dynamic require which is then happening inside the future
but if you do the dynamic require outside the future once, you won't have the problem inside the future anymore since the namespace is already loaded
I narrowed it down a bit:
$ clojure -M -e "(prn @(future (load-string \"(set! *warn-on-reflection* true)\"))) (shutdown-agents)"
true
This works in Clojure
But this doesn't:
$ clojure -M -e "(prn @(future (set! *warn-on-reflection* true))) (shutdown-agents)"
Now I need to find out why load-string
does accept this while literally executing the expression doesn'tYou can work around this with:
(require '[com.grzm.awyeah.client.api :as aws]
'[com.grzm.awyeah.credentials :as credentials]
'[clojure.spec.alpha]
'[com.grzm.awyeah.http-client]
'[com.grzm.awyeah.http.awyeah]
'[com.grzm.awyeah.protocols.json])
Posted an issue https://github.com/babashka/babashka/issues/1413
Interesting - thanks so much @U04V15CAJ. Yes, just adding protocols.json seems to fix it for me. I really appreciate you taking a look sir!
Another workaround:
(defn get-tasks
[& {:keys [profile], :or {profile "default"}}]
(aws/invoke (aws/client
{:api 'dms,
:credentials-provider (credentials/profile-credentials-provider "default")})
{:op :DescribeReplicationTasks}))
(defn main []
(print @(future
(binding [*warn-on-reflection* *warn-on-reflection*]
(get-tasks :profile "default")))))
Nice - I have not yet dealt with the binding function (relatively new to Clojure). Any pros & cons between the two workarounds, or really just pick one?
👍 thanks again
The example I gave was a distilled down version of a monitoring utility that calls a whole bunch of remote APIs and endpoints. So they run in parallel then report results.
(I tore everything out to try to narrow down the problem)
I’m really loving Clojure - we also use nodejs a lot where I work. So it’s been a fun challenge to decide which patterns make sense to carry into Clojure and which should be rethought. For example, I really like the “pretty much never blocking” nature of nodejs. So I’m tending to develop for Clojure in a similar sense when it comes to API calls. I’m still figuring out what does and doesn’t work well. I like that with futures + immutability I can develop something non-blocking and multi-threaded in Clojure - without too much hassle. That’s pretty awesome.
@U0250GGJGAE Cool. There is also #C029PTWD3HR for scripting on Node.js
Oh cool - I hadn’t run across that!
We’re running our own APIs in node, I haven’t yet had a moment to think on consider cljs - but it is definitely on my list to explore!
We have a lot of SPAs that could possibly take advantage.