This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-08-04
Channels
- # announcements (7)
- # babashka (32)
- # beginners (106)
- # bristol-clojurians (10)
- # cider (6)
- # clj-kondo (5)
- # cljdoc (10)
- # clojure (110)
- # clojure-australia (10)
- # clojure-dev (6)
- # clojure-europe (12)
- # clojure-nl (2)
- # clojure-norway (16)
- # clojure-spec (9)
- # clojure-uk (59)
- # clojurescript (105)
- # community-development (2)
- # conjure (46)
- # cursive (12)
- # data-science (1)
- # datalog (26)
- # datomic (37)
- # docker (4)
- # emacs (10)
- # events (1)
- # fulcro (8)
- # graalvm (2)
- # jobs (1)
- # jobs-discuss (1)
- # malli (24)
- # meander (13)
- # off-topic (52)
- # pathom (4)
- # polylith (17)
- # proletarian (4)
- # react (1)
- # rewrite-clj (4)
- # shadow-cljs (56)
- # sql (21)
- # xtdb (14)
I’m writing a clojurescript library from scratch (i.e. without a leiningen template) and I’m getting the following error:
Failed to compile build :dev from ["src/cljs"] in 8.073 seconds.
---- Could not Analyze dev-resources/public/js/out/cljs/pprint.cljs ----
Invalid :refer, var cljs.core/IWriter does not exist
Can someone help me figure out why I’m getting this error? In a previous thread someone said this error came from a directory structure issue, but I can’t find how that applies to my project.Can you post your project.clj file? It kinda looks like you have the output on the classpath
project.clj
(defproject cljs-spotify-sdk "0.0.1-SNAPSHOT"
:description "FIXME: write description"
:url ""
:license {:name "Eclipse Public License"
:url ""}
:dependencies [[org.clojure/clojure "1.10.3"]
[org.clojure/clojurescript "1.10.773"]]
:plugins [[lein-cljsbuild "1.1.7"]]
:clean-targets ^{:protect false}
[:target-path
[:cljsbuild :builds :app :compiler :output-dir]
[:cljsbuild :builds :app :compiler :output-to]]
:resource-paths ["dev-resources/public"]
:figwheel {:http-server-root "dev-resources/public"
:server-port 3449
:nrepl-port 7002
:nrepl-middleware [cider.piggieback/wrap-cljs-repl]}
:cljsbuild {:builds {:dev
{:source-paths ["src/cljs"]
:compiler
{:output-to "dev-resources/public/js/cljs_spotify_sdk.js"
:output-dir "dev-resources/public/js/out"
:optimizations :none
:source-map true
:pretty-print true}
:figwheel
{:on-jsload "cljs-spotify-sdk.core/load-sdk"}}}}
:profiles {:dev {:dependencies [[binaryage/devtools "1.0.2"]
[cider/piggieback "0.5.2"]
[figwheel-sidecar "0.5.20"]
[nrepl "0.8.3"]]
:plugins [[lein-figwheel "0.5.20"]]}})
(nm, figured out it was just weird layout)
I think it’s because the output is into the dev resources but it’s been so long since I’ve set up cljs build
Yeah, I'm super rusty. I last did serious cljs back in 2013/2014 and then tried to pick it up again maybe six months ago. Totally different world, hahaha!
What protocol do you use in a microservice setting, plain http or grpc? I’d like to know the performance boost of grpc is worth the effort.
grpc runs on top of http.
Is there anything similar to the & rest
vector destructuring, but for maps? Sort of like (but obviously not exactly)
(let [{a :a b :b & rest} {:a "a" :b "b" :c "c" :d "d"}]
{:a a :b b :rest rest})
=> {:a "a" :b "b" :rest {:c "d" :d "d"}}
you can use {:keys [a b] :as m}
and m
will be bound to the entire map. But maps with extra values usually flow through a system just fine
yeah that's kind of what I thought would be the philosophy, that there isn't usually need to remove keys. Thanks!
Hello everyone! I want to ask a question regarding the loop/recur form. Why people using Clojure prefer the loop instead of creating another recursive function? If my question sounds stupid please bear with me...I am trying to learn Clojure by myself! 😊
It's because Clojure does not have tail recursion, also called tail call elimination or tail call optimization.
If you write a recursive function, then the stack will grow with every recursive call
The loop/recur
system is a workaround for this; it lets you write loops in the recursive style, without doing actual recursion on the function level
(Tail call elimination could not be implemented for Clojure because it would have broken compatibility with Java libraries)
Also, this is not a stupid question. It is in fact one of the first questions that many experienced lisp developers will ask about Clojure. So don't worry!

Also note that loop
acts as a target for recur
but it is not strictly necessary. If you omit loop
then recur
targets the enclosing function.
But many recursive procedures have some initialisation that you don’t want to expose to the caller, instead of writing a separate function, you can simply embed loop
for this purpose.
@U7ZL911B3 could you please elaborate on "(Tail call elimination could not be implemented for Clojure because it would have broken compatibility with Java libraries)". I'd appreciate any details.
The JVM simply does not eliminate tail calls. So if you write a recursive procedure in tail position, it will create a stack frame for each recursive call.
tail call elimination would turn that into something like a while
loop that doesn’t fill up the stack each time it loops around.
There is a group that is working to change this: https://wiki.openjdk.java.net/display/loom/Main And here is where SICP discusses this concept (scheme has TCO): https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2.1
The way Rich put it is that the target for Clojure is "stack compatibility" with Java - Clojure functions and Java functions can completely coexist on the call stack. Callbacks between the two languages work as expected. Since the JVM has no native tail call facilities, implementing them on the JVM would have meant Clojure functions would have to be something different: not JVM functions. This would significantly hurt interop.
Good evening @U7ZL911B3 and thank you so much for your detailed explanation! I am really happy that my question made some sense. About the tail call optimization for the JVM I know that it's one of the things which are missing. But since it's not a feature which is there isn't the loop/recur form just syntactic sugar?
loop/recur is more than syntactic sugar - (recur) /must/ be a tail call, and tail call optimization /does/ happen in that limited context.
Oh! If let's say as a thought experiment, Clojure didn't care about compatibility with the Java libraries...this thing can be still implemented on the language level?
This important distinction is that 'recur' is not a function call. It can only exist in tail position, and it can only 'call' the enclosing loop point (either a loop
form or a fn
, which is an implicit loop point).
Yes, it could totally be done.
Right, so basically JVM doesn't support it, that's the issue here?
That's the reason for the original design decision, yes.
OK, got it. Thanks!
It's not clear to me what will happen if the JVM grows TCO support. loop/recur
has turned out to address a whole lot of those use cases, without much fuss, but not all of them (mutual recursion is one). So it would be nice to have real TCE in clojure. But there are also significant backwards compatibility concerns, and Clojure likes to maintain backwards compatibility for quite a while.
Oh! I see! I am glad to find all these little happy surprises! I thought that tail call optimization could not be done in Clojure because the JVM didn't supported it and thus the loop/recur form was just syntactic sugar! I am super glad I was completely wrong about it!!!
Hi @U01EFUL1A8M and thank you also for your input! Yes...Project Loom will be opening up many possibilities for the JVM not only for TCE but also green threading too!!!
is it possible to destructure a & blah
rest argument? something like (defn foo [a & [b]] (println b))
. i want to be able to pass in 1 or 2 arguments and have the second one be nil
or the object passed in and not a list of the object
It’s possible but it may have unintended consequences.
I say that trying to remember the blog post or whatever that says why destructuring &
args might cause you grief down the road.
Also, it will allow you to pass in > 2 args too, and all those after the second will be thrown away
Ah yes, that was one of the possible unintended consequences, you might drop arguments you didn’t intend to.
huh, i must have mistyped something cuz i thought i tried it in my repl
thanks! i’ll try it again
(defn foo [a & [b]]
(pr-str {:a a :b b}))
=> #'user/foo
(foo 1)
=> "{:a 1, :b nil}"
(foo 1 2)
=> "{:a 1, :b 2}"
(foo 1 2 3)
=> "{:a 1, :b 2}"
excellent, thank you!
(defn foo
([a] (pr-str {:a a}))
([a b] (pr-str {:a a :b b})))
=> #'user/foo
(foo 1)
=> "{:a 1}"
(foo 1 2)
=> "{:a 1, :b 2}"
(foo 1 2 3)
Execution error (ArityException) at user/eval39896 (form-init3538426389865140913.clj:1).
Wrong number of args (3) passed to: user/foo
i’m working with a multimethod, so i can’t do overloading, otherwise i’d do it as you have
@UEENNMX0T With "overloading", do you mean multi-arity defs? Because those seem to work for multimethods.
do you have a demonstration? the docs don’t say anything about how it works
hot damn, that’s nice
yeah, i saw that
What is an uberjar? OK, it's the sources packed into .jar
, but more specifically? What's the difference normal pack into jar/uberjar?
I know very little about Java.
it's the sources, dependencies, plus usually *.class files from compiling your clojure code
any recommended tutorials on setting up a socket repl for production deployment tasks?
@franco.gasperino What sort of "production deployment tasks" are you asking about?
I was hoping to launch an alias using :main-opts ["-m" "my.ns"] and pair it with :jvm-opts to launch the socket repl.
is it as straitforward as that seems?
Yes, although we don't use the CLI to launch stuff in production. We build and deploy uberjar files and start them with java -jar
and specify the Socket REPL stuff as a regular JVM option.
alright
before i go uberjar route, im just attempting to run the application on jvm startup (eval all required forms, fn entrypoint, etc) while still providing an interface where i can probe the running application via repl
wow ok, it works as expected
that is great. my brain hurts from the possibilities
is there a warning list on what you should not do in these configurations? I assume that a connected session could evaluate an existing form (runtime changes?). Are interactions with the reader atomic?
Well, there's the usual caveat of "changing code that is running, live in production is potentially dangerous" 🙂 If you eval anything that hangs up the REPL (e.g., trying to (println (range))
or some such), you're going to hang up your production app. If you eval anything that stops the process, it'll stop the app. And (obviously?) any evaluations you do that modify the database or filesystem etc are going to happen on the live system.
I haven't verified that def
is atomic/"safe" but we only do this on a relatively low-traffic app these days and we're not seen a problem due to just that aspect.
(we've messed up the production app a couple of times and needed to jump on the server and restart it... but "great power" and "great responsibility" etc 🙂 )
i dont have an immediate case, but only pondering...
(defn evens-only [x] (even? x))
(defn pos-only [x] (pos-int? x))
(def pipeline (comp (filter pos-only) (filter evens-only)))
(def channel (clojure.core.async/chan 1 pipeline))
(clojure.core.async/>!! channel 2)
(clojure.core.async/<!! channel)
;; Socket repl session..
=> (in-ns 'blah')
=> (defn- after-5-only [x] #(> 5 x))
=> (def pipeline (comp (filter pos-only) (filter evens-only) (filter after-5-only))
that type of ad-hoc mutation
runtime limited deployment work
Remember that with def
, the value will be compiled into code that uses it so, in general, when you def
a value (rather than defn
a function), the existing code will not see your change.
yes, i do understand that
https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs applies here, just as it would in a REPL session for development.
either a value or an expression which returns a value, the value would be retained even if the function was changed
perhaps a transducer wasn't the best example of my head scratching
ive been using your recommendation of rich comments instead of direct repl evals.
however, i was curious how to probe at a live running production app. so far, i can create CLI alias to run the app and combine it with a socket repl definition to connect to it
looks good. boxing my testing atom swap! reset! operations, it looks sane
What is the correct form in CLJS for calling a JS function with a variable number of parameters?
I am trying to call https://developer.mozilla.org/en-US/docs/Web/API/Element/before on a list of DOM elements.
(apply .before target params)
does not compile.
I found two solutions:
• (-> target .-before (.apply target (into-array params)))
• (apply js-invoke target "before" params)
Why does (let [fn-ref #'+] (fn-ref 1 2))
work? (Correctly produces 3
.)
I knew (let [fn-ref +] ...)
would work, but didn't expect the #'fn-name
to work for invocation (`(fn-name)`) as well.
(Turns out one can get fn metadata AND call it at the same time, as in (let [fn-ref #'+] (println (:doc (meta fn-ref))) (fn-ref 1 2))
. That saved me a lot of headache, but I still don't know why the hell does it work.
Vars implement IFn: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java#L20
This is the same reason you can use keywords in the function position of an invocation: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Keyword.java#L24
That roughly translates to "are callable" I take it?
yes, that's right
Cool! Thanks!
You can make your own thing work that way, if you like.
"You can make your own thing work that way, if you like." Any example of that?
goodness... I feel like I've done it before, but quickly deleted the code before anybody saw it 🙂
Haha OK
and "in real life" https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/model/user_manager.clj#L89
Great, thank you 🙏:skin-tone-3:
sometimes when refactoring it makes sense to replace a function with a record with an IFn implementation (like a closure, but you can create new closures with different "locals")
(ins)user=> (defrecord Foo [a b] clojure.lang.IFn (invoke [this] (+ a b)))
user.Foo
(ins)user=> ((->Foo 1 2))
3
(ins)user=> (-> (->Foo 1 2) (update :a + 39) (#(%)))
42
im currently going through Clojure for the Brave and True
and something isn't clicking for me:
(defn comparator-over-maps
[comparison-fn ks]
(fn [maps]
(zipmap ks
(map (fn [k] (apply comparison-fn (map k maps)))
ks))))
(def min (comparator-over-maps clojure.core/min [:lat :lng]))
(def max (comparator-over-maps clojure.core/max [:lat :lng]))
I thought the point of apply
is to invoke a function that typically expects a collection over a variadic number of arguments. What does apply comparison-fn (map k maps))
get you instead of (comparison-fn (map k maps))
?(min [1 2 3])
returns [1 2 3]
as it is saying "if there's only one element, it is the min." (min 1 2 3)
returns 1
for obvious reasons. If you have a collection [1 2 3]
, how would you call min
on it's elements as if they were arguments? (apply min [1 2 3])
is effectively (min 1 2 3)