Fork me on GitHub
Michaël Salihi09:06:15

Hi! Is there an equivalent to JS padEnd function in Clojure? If not, what's the idiomatic way? Use format?


(clojure.pprint/cl-format nil "~5a" 3) ;; => "3    "


unfortunately there is no cljs equivalent

Michaël Salihi10:06:35

Thx @U04V4KLKC. It seems equivalent to, right?

(format "%-5s" 3)

Michaël Salihi10:06:13

For CLJS, maybe we can interop.


wow, I didn't know that format supports that

👍 1
Michaël Salihi10:06:31

For the CLJ version with format, is it possible to specify another char than space ? Like the JS first example:

const str1 = 'Breaded Mushrooms';

console.log(str1.padEnd(25, '.'));
// expected output: "Breaded Mushrooms........"
or I must to chain with a clojure.string/replace?


it is possible with cl-format 🙂 (clojure.pprint/cl-format nil "~5,,,'.a" 3)

Michaël Salihi10:06:47

Perfect, thank you very much! 🙂

Martin Půda10:06:12

Or with org.apache.commons.lang3.StringUtils :

(StringUtils/rightPad "Breaded Mushrooms" 25 \.)
=> "Breaded Mushrooms........"

👍 2

what are the options for simple doc generation, been trying to use codox but keep getting hit by this issue so curious if there are other options or if codox is still my best bet ?


I don't have much exp with js frameworks. I am doing some minor tools in cljs in my corp. If the time comes to pitch something, what can I enumerate as the pros of clojurescript over js world in 2022?


By showing an example) clone some 5-8 years old cljs project and run it without changing a single line of code. That kinda luxury is highly uncommon in js world

👍 1

IDK. I did lots of JS+react+immutable.js work a few years ago. I've gone down many rabbit holes with clojurescript and I recently have been looking at the JS world again. The churn argument is definitely not (as severely) present in JS anymore (assuming you're using react) For me personally, the projects I was working on were react + redux and those have the same APIs from years ago. There are tradeoffs with any tools. the biggest issue I've had working with clojure and cljs at work are social issues. Most devs don't care about advances in tech and using simpler ideas. In my mind the benefits are obvious to using clj/cljs but most devs don't see that. I think the most important thing to ask is "what problem are you trying to solve?" if you want sane architecture and lots of resources for having your team learn and share and look up their issues I would suggest using JS + react + redux + immer. Aside from lack of a standard lib and no macros, JS has adopted lots of the things that make cljs great while removing some of the headaches of using cljs (is it goog.object/get or .-property ? "Can I use this ES6 library??" "how can I use storybook? XState?) anwyay, I'd suggest looking into JS, I have been quite enjoying not having to think about tooling and interop and all the translation layers from using it. The big caveat is if you want to do fullstack clj + cljs - then I would agree you're going to get leverage from one language (and data assuming you're sending transit encoded data over the wire) If you haven't seen it yet check out simple made easy - the part about emphasizing that ultimately you're building software for humans to use and they don't care at all about the tech you use applies here exactly. "Not everything is awesome" - this also applies to clojure and its libraries!


This is missing the point. You of course are free to cherry-pick egregious examples of breaking APIs (for some random unstated library), but this is a strawman, not a steelman. I stated that React and redux have been stable for many years. This is still true. In fact almost all cljs ui libs use react and it has remained stable for them. Clojure also has churn: • lein -> deps.edn • prismatic schema -> spec.alpha -> alpha.spec (spec 2?) My point is that all tools have tradeoffs - including clojure. This question was specifically about clojurescript. I argue that clojurescript is not simple the way clojure is, by the Hickeyan definition. Clojurescript interleaves a language with the implementation. I'm not talking about the fact that you have interop - that's a feature. I'm talking about the fact that you have to be aware of the quirks of the libraries the compiler is building upon (google closure). And because google closure is barely used in the world of javascript you also get to interleave in your favorite other JS bundler (Webpack has its own page on!) The tweet you mentioned is a perfect example of what I experienced at my day job, the unmentioned developer who was attempting some seemingly simple tasks gets their world flipped upside down until a resident expert comes in to help. At work we have a ~40k LOC re-frame app and most devs on the team don't understand how the compiler toolchain works or how the non-g closure libs are brought into the build. We have one or two resident experts on CLJS config and everyone leans on them. So my point is, you're not just choosing the semantics of the language when you're choosing cljs - you're also adopting its version of tooling hell. It may be a different flavor than javascript, but to pretend it is all roses is dishonest. When Rich chooses to he calls this "leverage" (like how datomic ions is interleaved with AWS) instead of saying it is complected. This seems like an arbitrary choice based on the biases of the author. Here's a ridiculous example from deps.edn: Add a colon to a package like: {:org.clojure/clojure {:mvn/version "1.10.1"} and you'll get this brilliant error: clojure.lang.Symbol (clojure.lang.Keyword and clojure.lang.Symbol are in unnamed module of loader 'app') java.lang.ClassCastException: class clojure.lang.Keyword cannot be cast to class clojure.lang.Symbol (clojure.lang.Keyword and clojure.lang.Symbol are in unnamed module of loader 'app') at clojure.lang.Symbol.compareTo( I enjoy using clojure and clojurescript everyday, but let's be honest with comparisons. I mentioned simple made easy because it is directly relevant. We should clear about the tradeoffs made when choosing tools and focus on what problem we are trying solve and the humans who will use our solutions.


I can't speak from the trenches like you are. I've stopped using re-frame when they introduced interceptors. Consistency of Reagent API is remarkable it seems. I don't like hiccup for react. I use shadow-cljs for compiling and helix for react. I've fought many times to make repl work in cljs (I'm vim user) until I gave up. Repl is just for clojure which is a pitty. I find clj-kondo a phenomenal tool. I add typescript to the churn of javascript. If you are not a specialist, you benefit from it with vscode oracle of syntax. The problem is when you need to read/change the source. The achievements of @U0ETXRFEW with calva are simply beautiful. I just don't go for it because I would have to live without "my paredit". I love navigating the code through the parentesis and structured editing is a must imho in clojure. But if you ask me I miss my Delphi. Developing became a frustating activity compared to old days of client server architecture. The complexity exploded. These frameworks and librarys need the "religion motivation" to sustain, and they play this card. I myself fell into this trap. I followed google with GWT, I tried to use spring, javascript in the early days. Tons of complexity. Clojure is my Delphi :)


Wait... I thought Calva Paredit was on par with the other implementations out there. What are you lacking, @geraldodev? (Not trying to convince you to use VS Code and Calva. I'm only interested in what Calva can do better.)


(Dunno why that was also sent to the channel. Not intended!)


@U0ETXRFEW I tried to use calva a while ago, and I think it changed a bit since that time.I remember it relying on a mechanism that allowed the breakage of the balance of the parantesis. I've checked your docs, and you warn about vim integration and point to neovim integration. I would have to use it again to say that it enforces the structure of my beloved parentesis on par with paredit.


The balance check is pretty raw, for sure. Would be nice to know if you think it is too raw.


What I love about paredit is that I can wipe the rest of the line and it works. That combined with optionality of commas in clojure makes editing clojure code a pleasure. I've paired with co-pilot in javascript, and the service it provides is syntax completion, mostly commas which clojure does not need.


There is a kill-rest-of-line command in Paredit. I think it should be doable to map it to D and have that safe wipe rest of line you want.


(default bound to ctrl+k on Mac and, iirc, ctrl+k ctrl+k on windows/linux).


I seldom use this command myself for some reason.


Just to tie it back. To quote David Nolen ( "you can do this, you can build a functional relational architecture now with just node.js, use the various functional libraries there, and you can use react"

Jim Strieter20:06:17

Assuming I already know which index or key I want to use, is there a significant performance difference between vector and map for random lookup? For instance, is looking up a value from a map > 10x slower than a vector?


Looking up an item in a vector depends on the length of the vector. It is not a constant factor between the two


oh sorry i missed using the index for the vector


quick profiling it actually looks like vector is faster

Jim Strieter20:06:59

Does profiler say how much?

Jim Strieter20:06:08

Hey what profiler do you use?


keep in mind that vectors only work on integer keys


and work better when dense


enumerate=> (let [v-coll (vec (range 10000))
                  m-coll (zipmap (range 10000) (range 10000))]
              (println "vector index")
              (dotimes [_ 10]
                  (dotimes [_ 5000]
                    (nth v-coll (dec 10000)))))
              (println "map key")
              (dotimes [_ 10]
                  (dotimes [_ 5000]
                   (m-coll (dec 10000))))))
vector index
"Elapsed time: 0.412875 msecs"
"Elapsed time: 0.2795 msecs"
"Elapsed time: 0.270167 msecs"
"Elapsed time: 0.269083 msecs"
"Elapsed time: 0.273584 msecs"
"Elapsed time: 0.269541 msecs"
"Elapsed time: 0.269416 msecs"
"Elapsed time: 0.269667 msecs"
"Elapsed time: 0.278083 msecs"
"Elapsed time: 0.278333 msecs"
map key
"Elapsed time: 0.595833 msecs"
"Elapsed time: 1.251916 msecs"
"Elapsed time: 1.241833 msecs"
"Elapsed time: 0.577583 msecs"
"Elapsed time: 0.562875 msecs"
"Elapsed time: 0.541792 msecs"
"Elapsed time: 0.53325 msecs"
"Elapsed time: 0.536333 msecs"
"Elapsed time: 0.553042 msecs"
"Elapsed time: 0.534959 msecs"

🙌 1

just quick and dirty. feel free to use criterium or some more advanced profiler. If you’re in a hot patch perhaps using a different data structure would give you better results

Jim Strieter20:06:18

Looks like roughly a factor of 2

Jim Strieter20:06:06

For what I'm doing that's acceptable. My project has a lot of maps in it already and refactoring at this point would be a lot of work.

Jim Strieter20:06:18

I can always refactor later if there's a reason to.


its possible my constant access of one particular key excercised non-arbitrary code paths. perhaps vectors are fast on the last key but in the middle are much slower. maybe maps are naturally faster but that particular key has a few more hash collisions than in general

Jim Strieter21:06:07

Possible. I'm not super worried about it though.

Jim Strieter21:06:32

I am doing this:

(map f coll)
I want to take every item in coll and turn it into a list of expressions like this:
((conj thing-1)
 (conj thing-2)
 (conj thing-n))
Finally, I want to thread some input through that list:
(-> some-input
    (conj thing-1)
    (conj thing-2)
    (conj thing-n))
Here are my questions: 1.) How do I turn coll into the list of (conj thing-x's)? 2.) How do I take that resulting list and thread an input through every function in the list? For instance, the following does not produce a numerical output:
(-> 1 (list inc inc))
(1 #object[clojure.core$inc 0x7e2360f4 "[email protected]"] #object[clojure.core$inc 0x7e2360f4 "[email protected]"])


expressions are "just" lists so you can use the normal list operations. have you looked into syntax quote? it's a little more effort to learn, but it's also very convenient for code that produces code. Here's the official link, There's also some related links there that might be helpful.


I think you just want comp


(apply comp (map #(fn [x] (conj x %)) things))

🙌 1

which is the same as

(apply conj ... things)

Jim Strieter21:06:54

When I do this:

(apply comp (map #(fn [x] (conj x %)) (list {:a 0 :b 0} {:a 99} {:c 42})))
I get this:
#object[clojure.core$comp$fn__5825 0x395878b2 "[email protected]"]
I know this is dumb but how do i display a map value?


it isn't a map


it is a function that when called will conj

{:a 0 :b 0} {:a 99} {:c 42}
on to whatever you pass in

🙌 1

user=> ((apply comp (map #(fn [x] (conj x %)) (list {:a 0 :b 0} {:a 99} {:c 42}))) {::whatever 1})
{:user/whatever 1, :c 42, :a 0, :b 0}

🙌 1

user=> (apply conj {::whatever 1} (list {:a 0 :b 0} {:a 99} {:c 42}))
{:user/whatever 1, :a 99, :b 0, :c 42}

🙌 1
Jim Strieter21:06:12

Oh cool! I just tried:

((apply comp (map #(fn [x] (conj x %)) (list {:a 99} {:c 42}))) {:a 0 :b 0})
and got:
{:a 99, :b 0, :c 42}
which is the desired result. Thank you so much! 🙂


user=> (reduce conj {::whatever 1} (list {:a 0 :b 0} {:a 99} {:c 42}))
{:user/whatever 1, :a 99, :b 0, :c 42}


Wait... I thought Calva Paredit was on par with the other implementations out there. What are you lacking, @geraldodev? (Not trying to convince you to use VS Code and Calva. I'm only interested in what Calva can do better.)