This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-17
Channels
- # announcements (2)
- # aws (4)
- # babashka (20)
- # beginners (256)
- # calva (15)
- # chlorine-clover (1)
- # cider (12)
- # clj-kondo (25)
- # cljsrn (16)
- # clojure (115)
- # clojure-europe (7)
- # clojure-finland (5)
- # clojure-italy (10)
- # clojure-nl (35)
- # clojure-spec (13)
- # clojure-uk (83)
- # clojurescript (42)
- # code-reviews (81)
- # conjure (8)
- # cursive (6)
- # data-science (1)
- # datomic (3)
- # duct (18)
- # emacs (2)
- # figwheel-main (40)
- # fulcro (21)
- # helix (1)
- # jackdaw (7)
- # java (2)
- # lein-figwheel (5)
- # luminus (1)
- # observability (12)
- # off-topic (32)
- # parinfer (5)
- # pedestal (19)
- # re-frame (5)
- # reagent (8)
- # reitit (17)
- # rewrite-clj (47)
- # rum (19)
- # shadow-cljs (48)
- # spacemacs (4)
- # sql (40)
- # tools-deps (22)
- # vrac (1)
- # xtdb (25)
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:
https://github.com/clojure/clojure/blob/master/src/clj/clojure/repl.clj#L19
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
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?
I don't know about Clojure solution, however jvm support hotswap, https://www.jetbrains.com/help/idea/debugger-hotswap.html https://stackoverflow.com/questions/42293054/how-to-hot-deploy-code-in-tomcat-running-in-debug-mode , it has limitations you can only modify existing methods (not add new methods), but I found it useful.
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: https://github.com/clojure/tools.namespace. I am looking more for opinion if what I try is not something idiotic that will bite me down the road 🙂
Have you considered using something like https://github.com/stuartsierra/component (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
Got it 🙂 I am not very experienced with tests but I assume you know about https://clojuredocs.org/clojure.core/with-redefs ?
yeah - but that introduces another set of hurdles, I would need to dynamically inspect the namespace to find all possible values to redef
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
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
;; ----------------------------------------------------------------------------
(defn load-from-classpath
"Loads the default config from the classpath"
[]
;; Eagerly loads and validates values
(ConfigFactory/invalidateCaches)
(let [^Config config (ConfigFactory/load)]
{:server-port (.getInt config "server.port")
:db-pool-connections? (.getBoolean config "db.pool_connections")
:db-name (.getString config "db.name")
: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."
[key]
(fn [config]
((if (instance? IDeref config)
(deref config)
config)
key)))
;; ----------------------------------------------------------------------------
(def ^{:doc "The port on which to listen for http requests."}
server-port (extract-key :server-port))
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)
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
(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:
/home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/clojure/homebrew-tools/Formula/clojure.rb:7
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)'
2
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)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?
yeah, sorry I actually skipped over the def entirely
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 don't think this is particularly good code as it's hard to understand, so I would probably still say don't do this :)
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"
I guess the right mental model is that :or
always refers to the local binding in the same destructure form
(let* [map__210260 {:a 1}
map__210260 (if
(clojure.core/seq? map__210260)
(clojure.lang.PersistentHashMap/create
(clojure.core/seq map__210260))
map__210260)
a (clojure.core/get map__210260 :a)
b (clojure.core/get map__210260 :b (inc a))]
b)
@ivana geez, I almost forgot let was a macro thanks 😉. But also don't forget that maps are unordered.
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
my final reply: https://github.com/borkdude/clj-kondo/issues/915#issuecomment-645438056
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))]
b)
I think these discussions are giving rise to a linter warning: https://github.com/borkdude/clj-kondo/issues/916
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 🙂
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. clojure.java.shell/sh
)? 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.
more info here: https://github.com/borkdude/clj-kondo/tree/master/analysis (see :var-usages
)
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?
channels don't loop, so what's looping?
so it has a reference to the channel
and it can't get gc'ed
i see, thanks @alexmiller
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 "clojure.main.report" "stderr")
to get stacktraces written to stderr rather than a temp file?
and if trying to get this in through lein is it lein update-in :jvm-opts conj '"-Dclojure.main.report=stderr"' -- 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
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
bingo. Db
is the result of deftype. thanks @hiredman, i required its parent namespace which solved my problem.
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
?
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 {})
true
(ins)user=> (instance? clojure.lang.IObj {})
true