This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-06
Channels
- # aleph (1)
- # announcements (29)
- # babashka (39)
- # beginners (52)
- # cider (3)
- # cljsrn (19)
- # clojure (167)
- # clojure-europe (15)
- # clojure-nl (2)
- # clojure-uk (62)
- # clojurescript (13)
- # community-development (8)
- # cursive (5)
- # datomic (10)
- # introduce-yourself (1)
- # java (10)
- # jobs (12)
- # jobs-discuss (1)
- # kaocha (2)
- # lsp (6)
- # luminus (1)
- # malli (15)
- # meander (3)
- # music (1)
- # nrepl (2)
- # off-topic (91)
- # pathom (4)
- # reagent (21)
- # reitit (10)
- # sci (5)
- # shadow-cljs (17)
- # spacemacs (3)
- # sql (7)
- # tools-deps (40)
- # utah-clojurians (2)
- # xtdb (7)
how would I mutate a string in clojure, get the result, and then mutate the result in a loop?
i think this is doable with a reduce, but the syntax is a bit clumsy and think there may be a better way to do this
in an imperative language, I would use a for loop and reassign a variable:
var x = "hello world";
var arr = ["l", "o"];
for (y in arr) {
x = x.replace(y, "")
}
is there anything like reduce that has the function as the last argument? it becomes a bit difficult to read when the arguments provided are place after the functionality
Use a thread last macro
Eg (->> 10 (range) (filter odd?) (map #(* 2 %)) (reduce +))
Hi. I need to write spec (for reitit route coercion, but maybe it didn't matter). The problem is - my parameter contains square brackets, like ...&page[p]=10...
For simple type checking (symbol "page[p]") int?
works just fine, but when I try to write a more complex rule using s/def
I got errors about namespace. I need to check the type and make the parameter optional. For simple symbols (without those brackets) all work just as needed.
i.e.
That works
(s/def ::level int?)
(s/def ::page int?) ;no brackets
(s/def ::query-params (s/keys :req-un [::level] :opt-un [::page]))
That works but checks only the type
inside route data {:get {:parameters {:query :level int? (symbol "page[p]" int?)}...
That not
(s/def ::level int?)
(s/def (symbol "page[p]") int?) ; got error
(s/def ::query-params (s/keys :req-un [::level] :opt-un [(symbol "page[p]")]))
clojure spec is designed to, among other things, validate clojure data, not as way to transform an arbitrary data (strings) into clojure data. For that you would use something like https://github.com/Engelberg/instaparse
s/def
is a macro so it's not going to evaluate that form.
The error Assert failed: k must be namespaced keyword or resolvable symbol (c/and (ident? k) (namespace k))
occurs when I try to evaluate the spec in the repl, or when call the router
Yes, because s/def
is a macro and it expects a qualified keyword or symbol -- not a list. Macros do not evaluate their arguments.
What is the proper way to align map keys and values if the line is starting to push past the 80 char width limit? I couldn't find anything specific about this in the style guide.
I don’t think you’ll find a one size fits all solution, it depends on the context.
If the map starts already highly indented, you might want to change the indentation before it (eg adding line breaks, or refactoring the code).
If you have specially long key names, you might want to put keys and values in their own line, with a space between key/value groups.
Or even extract the map and put it in a def
or a let
binding.
why is this working for “unchunking” a sequence (from joy of clojure)
(defn seq1 [s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (seq1 (rest s))))))
i would have thought when extracting out x
from (seq s)
it should have been realizing the first chunk — it is clearly not the case, but why?(chunked-seq? (range 1 100)) ;; => true
(chunked-seq? (seq (range 1 100))) ;; => true
(take 1
(map
(fn [i] (println "realized: " i) i)
(range 1 100)))
;; realized: 1
;; ...
;; realized: 32
;;=> (1)
(take 1
(map
(fn [i] (println "realized: " i) i)
(seq1 (range 1 100))))
;; realized: 1
;;=> (1)
It is not realizing the first chunk when x is destructured because it is inside the lazy-seq
macro, if it was say
(defn seq2 [s]
(when-let [[x] (seq s)]
(lazy-seq (cons x (seq1 (rest s))))))
then the first chunk would be realizedAlso regarding the realized: 1
and so on getting printed, is because the REPL will print the result (the P in REPL) realizing the lazy sequence.
To see the difference, try,
(def a
(take 1 (map
(fn [i] (println "realized: " i) i)
(range 1 100))))
(def b
(doall (take 1 (map
(fn [i] (println "realized: " i) i)
(range 1 100)))))
@UGSM2S2CS notice that the printing is happening outside the chunk realization - the first 32 elements of the range are being realized, but the side effect is still only walking 1 at a time, instead of a chunk at a time
what the "dechunk" is doing is clever (as so many things in that book are, it's a great book), but usually the better answer is to ignore chunking and simply not use laziness if the timing of realizing elements effects code correctness
Okay I think I found why, so this bit in the map
function
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (int (count c))
b (chunk-buffer size)]
(dotimes [i size]
(chunk-append b (f (.nth c i))))
(chunk-cons (chunk b) (map f (chunk-rest s))))
(cons (f (first s)) (map f (rest s)))))
Checks if s
is a chunked-seq?
but s which is (seq (seq1 (range 1 100)))
in our case is false.right, the chunking is honored by the various lazy-seq producing functions, and they cooperate by also chunking
And hence it uses (cons (f (first s)) (map f (rest s)))
to build the lazy-seq and that is one element at a time
what the code does is ignore that chunking, so it doesn't propagate
I would reject any code that dechunks in code review and ask that laziness not be used at all
thanks both! i got it eventually. i definitelly wont do anything like this, just wanted to understand whats going on
Is there a better way to do this?
(let [b (foo-fn 42 a)
smileys (bar-fn b)]
(for [smiley smileys]
(str "hello" smiley)))
I think I might be able to use the ->>
form?(->> (foo-fn 42 a)
bar-fn
(map #(str "hello" %)))
Or even
(->> a
(foo-fn 42)
bar-fn
(map #(str "hello" %)))
if you want to highlight that a
is being threadedActually sorry my example was not representing my code well. I will try again:
(let [b (foo-fn 42 a)
smileys (if criteria (reverse b) b)]
(for [smiley smileys]
(str "hello" smiley)))
not everything can be threaded, and sometimes it's better as a let either way
(map (partial str "hello") smileys)
If you really like to thread, you could extract the (if criteria ...)
into a function, then call (->> a ... (revert-if-criteria) ...)
(defn make-smileys
[b]
(if criteria (reverse b) b))
(->> (foo-fn 42 a)
make-smileys
(map #(str "hello" %)))
or
(-> (foo-fn 42 a)
(cond-> criteria (reverse))
(->> (map (partial str "hello")))
the various "arrow macros" like ->>
, as->
, cond->
etc. were all designed so that they could be used inside ->
I'd personally solve it like this. I break this down into two operations because I don't like mixing in conditionals with "must run" steps. It makes it hard to follow whats going on. Here it's very clear what smileys are (when reading code I want to first know what smileys are, and only then do I care about what order they are in).
(let [smileys (->> a
(foo-fn 42)
(map #(str "hello" %)))]
(if criteria
(reverse smileys)
smileys))