Fork me on GitHub

Is there any way to retrieve the arglists of special forms like try or defn?


defn isn’t actually a special form, so its arglists can be retrieved from its var meta data, (:arglists (meta #'defn))


getting the arglists for try is a bit trickier, if you look at the source for clojure.repl/doc, you’ll see that the arglist for some of the special forms are hardcoded:


Ah correct, defn is not a special form. It is in babashka, which is why I got confused. Thanks for the link to special-doc-map, it's probably what I need


having a special case for the special forms (there’s not many) seems reasonable or ignoring that special-doc is private might be an option ((resolve 'clojure.repl/special-doc) 'try) since core clojure code is pretty stable, but there is a slight risk since it is marked as private


(if anyone was curious, i did in fact crash and burn)


I have inherited a dusty, old Java project that produces a .WAR file. The whole cycle of edit 1) Java code, 2) recompile, 3) build .WAR file, 4) copy it to the relevant folder, and 5) restart Tomcat… seems like a terrible slow feedback loop. Is there any way that I can shorten this feedback loop using Clojure? Like can I take the project code inside a Clojure REPL and dynamically build and redeploy the project?

Matej Vasek11:06:07

I don't know about Clojure solution, however jvm support hotswap, , it has limitations you can only modify existing methods (not add new methods), but I found it useful.


Interesting. Thank you for pointing that out.


you should be able to eliminate building the war file and just point jetty to the files on disk (you would still have to compile the java)


It’s a web service that I need to probe a lot while developing, that’s why I need a much shorter feedback loop.


Hey guys, first of I would like to express my appreciation for this place 🙂 I am trying to bring some sanity to a large codebase. There is a namespace config, which has a bunch of atoms and functions to access those. My original approach was to eliminate the need for atoms, so I want to load the config file (single file), provide a function get-config that memoizes load-config and redefine all functions accessing values as plain values. This works kinda fine, we are losing some flexibility in terms of loading -> since now we need to restart application to reload config, - that's fine for the large part. However now I am sitting on a different problem - lots of tests mocked the namespace by simply changing the value of atom previously. Simple enough, now its more problematic since the initialization of namespace is loading config value and previous functions accessing certain values were converted to values ~defn~ def since it seemed more natural to access config/server-port rather than (config/server-port) . Any suggestions? To add a bit - I am looking for a facility that would let me reload/refresh an entire namespace so that the values initialized can initialize again according to modified config file - since I can change the actual config file and that would be decent enough. I found this which kinda serves this purpose: I am looking more for opinion if what I try is not something idiotic that will bite me down the road 🙂

👏 3

Have you considered using something like (might be a lot of work to integrate, but I think you can start gradually even if it’s not the perfect approach)


@U050KSS8M yeah, other parts of application will most likely land there in the end - but I consider the parts that land there "stateful".... config is mostly not stateful and application does not require for it reload during runtime... so I think its a waste to "make it stateful" just for the testing piece


I just require some trivial reload of the namespace so that values can reinitialize


Got it 🙂 I am not very experienced with tests but I assume you know about ?


yeah - but that introduces another set of hurdles, I would need to dynamically inspect the namespace to find all possible values to redef


I see… sounds like a lot is going on there


not really, just a couple of values get defined, but redef is not really ergonomic, since you have to always push a block + remember to append values when you add them, where current mocking lib, just produces new config and this config could be easily incorporated given that I can reliably reload namespace in tests


Super lazy approach - you can use load-file


There are benefits to the atoms if you actually want the dynamism


but in general you just need to "drill through" the config or "part of app specific configs" in function arguments


component and co are really good for organizing, but the end result is just a map of components


if you don't have that many moving parts you can make that map manually


(at least thats the epiphany i've had)


so instead of (config/server-port) or config/server-port


(conifg/server-port config)


then in the config namespace you can just have


(def server-port :server-port)


so what i've been doing in my "mess around" repo is


;; ----------------------------------------------------------------------------
(defn load-from-classpath
  "Loads the default config from the classpath"
  ;; Eagerly loads and validates values
  (let [^Config config (ConfigFactory/load)]
    {:server-port (.getInt config "server.port")
     :db-pool-connections? (.getBoolean config "db.pool_connections")
     :db-name (.getString config "")
     :env (or (opt-string config "env") "production")}))

;; ----------------------------------------------------------------------------
(defn- extract-key
  "Helper to extract a key from either a managed reference
  to a config or an actual immutable config."
  (fn [config]
    ((if (instance? IDeref config)
       (deref config)

;; ----------------------------------------------------------------------------
(def ^{:doc "The port on which to listen for http requests."}
  server-port (extract-key :server-port))


so you can pass around a config as is given by load-from-classpath


or you can pass around a config within an atom


the code reading it is generic over both cases


and if you pass an atom you can have a background thread reloading the config every now and then or something


so whatever parts of the config that aren't just read at startup can be altered without a restart (noting that in general avoiding a restart while changing something like server-port would be pretty darn hard)


sorry for all the small messages, i'm from the texting generation


also missed the part about you not wanting it to be stateful - don't have a good answer for you there


if you want to mock out a global in some way it needs to either be a parameter and passed to functions


or it ideally would be a dynamic var you could rebind


(def ^{:dynamic true} *config* (load))

(defn server-port [] (:server-port *config*))

;; And then in your tests
(binding [*config* (load-differently)]
  (... (is (= (config/server-port) 8000))))


Looking for suggestions on how to do server-sent events and HTTP/2 in Clojure. Currently I’m using Ring with Reitit, so ideally I’d like to use something that’s compatible with that stack 🙂


…or maybe I should just go with WebSocket?


I installed clojure tools from brew. Lately, I've been getting the following warning message:

Warning: Calling 'devel' blocks in formulae is deprecated! Use 'head' blocks or @-versioned formulae instead.
Please report this issue to the clojure/tools tap (not Homebrew/brew or Homebrew/core), or even better, submit a PR to fix it:

Alex Miller (Clojure team)14:06:17

this is due to a recent change in brew, we are aware

👍 3

it was established in previous discussions that this was undefined behavior in destructuring:

clj -e '(let [{:keys [a b] :or {b (inc a)}} {:a 1}] b)'
i.e. the pattern of using previously destructured bindings in defaults.


but what about this one:

(defn- a [] 1)
(let [{:keys [a] :or {a a}} {}] (a))'
I'm not suggesting one should do this, but this was reported at clj-kondo (clj-kondo thinks the var a is unused)

Alex Miller (Clojure team)15:06:27

behavior of :or bindings that depend on symbols bound in the destructuring is undefined


in this case it depends on the var, which happens to have the same name as a binding


but since it's not clear without looking at the implementation what this does, it might also be better to mark it as undefined?

Alex Miller (Clojure team)15:06:53

yeah, sorry I actually skipped over the def entirely


still agree with undefined tho?

Alex Miller (Clojure team)15:06:01

the semantics of :or are to provide default values that will be bound to the new local binding in the case where the key is not found in the map. because the local has not yet been bound at the point where the :or value is evaluated, I think it is reasonable to expect this to refer to the var a in this case.


I'll re-open the issue and fix it then

Alex Miller (Clojure team)15:06:24

I don't think this is particularly good code as it's hard to understand, so I would probably still say don't do this :)


it's kind of ambiguous what a is referring to: the previous binding or not


(I know the REPL output, it's the var that gets bound)


given that {} is unordered, and the :or could be executed before or after the :keys, I find that surprising


but of course that's a bad mental model for the compiler here, and the real solution is just "don't do that"


    '(let [{:keys [a b] :or {b (inc a)}} {:a 1}]


I guess the right mental model is that :or always refers to the local binding in the same destructure form


dont forget that let is a macro


(let* [map__210260 {:a 1}
         map__210260 (if
                      (clojure.core/seq? map__210260)
                        (clojure.core/seq map__210260))
         a (clojure.core/get map__210260 :a)
         b (clojure.core/get map__210260 :b (inc a))]


@ivana geez, I almost forgot let was a macro thanks 😉. But also don't forget that maps are unordered.


Yep. And I also agree with "dont do that" 🙂


right - the destructure code ends up accessing the map by keys in a specific order, rather than consuming the map in the order of seq


one could argue that if one uses a default name equal to the binding, the code will guarantee that it's not one of the previous bindings, but one really has to dig into the code to see it


there can be imagined a lot of cases, when you have to dig into the code to see the behaviour, or even there is UB. and it seems, that it can be as UB, and "dont do that" case. anyway you have dozen way to rewrite it in more readable way, f.e.

(let [{:keys [a b]} {:a 1}
        b (or b (inc a))]


I think these discussions are giving rise to a linter warning:


And there can be, that in next clojure core release :or destructuring can be refactored and the result can be different. I can show you one more funny case with different results using ->> and as-> macro as well, if you want 🙂


more than welcome to post such examples in the issue I just posted 🙂


It is not related to :or case, so it is the example of general unintuitive behavior at all 🙂


Can anyone recommend a linter (primarily clj, but cljs too would be a bonus) that can be configured to fail on usage of specific functions (e.g. It should be able to identify these vars regardless of :as alias or being :refer-red.


I've started one based on rewrite-clj, but this feels like something that should already exist.


@clojurians-slack100 you can use clj-kondo's analysis output for this

❤️ 3

it can lint clj, cljs and cljc


together with babashka you can write a very fast script from this.


I'll have a look and probably come annoy you with further questions 😉


(I'm an avid babashka user 🙂)


If I start a core.async channel that loops for a long time, but don't keep a reference to the channel, can it get GCed and stop?

Alex Miller (Clojure team)16:06:59

channels don't loop, so what's looping?

Alex Miller (Clojure team)16:06:24

so it has a reference to the channel

Alex Miller (Clojure team)16:06:04

if your go loop ends and nothing else refers to the channel, then it will be gc'ed


to confirm, we can set (System/setProperty "" "stderr") to get stacktraces written to stderr rather than a temp file?


makes our logs from jobs in the cloud not very helpful


but setting this system property will change this behavior, yes?


(making sure it doesn't need to be set on jvm startup or anything)


and if trying to get this in through lein is it lein update-in :jvm-opts conj '""' -- repl?


wouldn't it be easier to update the jvm-opts for your task in project.clj, or use an uberjar to run your repl instead of lein?


kinda. this is in a bit of code that is comparing across git versions so any changes i add to project.clj or code don't get seen


until that change makes its way onto master


i'm trying to test the class of an object (= datomic.client.impl.shared.Db (class my-db)), but i get a ClassNotFound exception for datomic.client.impl.shared.Db despite having the datomic.client.api library required in my project. i've also tried :import ing the class directly. where am i going wrong?


the class is likely generated by loading clojure code, if you haven't loaded the clojure code that creates it, then it won't exist


(a deftype or defrecord)


bingo. Db is the result of deftype. thanks @hiredman, i required its parent namespace which solved my problem.


you doing something with spec @joshkh ?


if so, might be more robust to check for an interface/protocol rather than concrete class


not quite, i'm working on serialising function arguments for a k/v cache, however checking concrete classes has been a pain. are you referring to instance? rather than class?


(supers (class your-db)) <- instance checking on one of the interfaces in there

👍 3

that's exactly what i was looking for. not just for "edge case" classes, but also for checking when objects fall in the bucket of native clojure data structures. thanks a lot. 🙂


yeah, instance? is what you want for that

(ins)user=> (instance? java.util.Map {})
(ins)user=> (instance? clojure.lang.IObj {})

👍 3

cool. i nailed down IObj for testing for metadata support. sounds like i'm heading in the right direction.