Looks good overall. A few small suggestions:
* If you want to terminate request processing early, it's good practice to use terminate
(http://pedestal.io/api/pedestal.interceptor/io.pedestal.interceptor.chain.html#var-terminate).
* You might consider doing something like (assoc-in context [:request :parsed qstring] parsed-param)
so that you're just adding data to your context map in the interceptor and not overwriting your request data.
* Consider the option of separating the concerns of parsing the query params and validating them into separate interceptors.
Also, there's a #pedestal channel that might be a good place to discuss further.
All good point. I've read somewhere that Pedestal terminates if the response object is there, that's why I followed the convention
@codonnell I do not understand the second point — how would assoc overwrite the request data?
I'm not a pedestal expert and it's been a while since I've used the library. IIRC the request data is available in ctx
under the :request
key. If that is the case and you do (assoc ctx :request {:parsed parsed-param})
, then you will dump whatever data was previously associated to :request
. If that's not what's happening, then there is no problem. :)
(fn [o] (or (vector? o) (list? o)))
I'm reading Chapter 6 from Joy of Clojure and in the section about laziness they claim that next
is "less lazy" and should print three dots in the example below - but for me it prints also only two dots. Is that something that has been changed in a more recent version of Clojure?
;; rest vs. next (p. 126)
(def very-lazy (-> (iterate #(do (print \.) (inc %)) 1)
rest rest rest))
;;=> prints two dots ..
(def less-lazy (-> (iterate #(do (print \.) (inc %)) 1)
next next next))
;;=> prints two dots too?!
Although this works as expected (= stated in the book):
Actually, this doesn't work either 😮
(println (first very-lazy))
;;=> prints ".4"
(println (first less-lazy))
;;=> prints ".4" (although it should print just "4")
iterate has been rewritten since JoC was written
it's now both seq and self-reducible and written in Java, so all of the details are different
changed in 1.7
Interesting, thanks!
the point is still generally valid :)
Yeah, I think it's visible here:
(defn simple-range [i limit]
(lazy-seq
;; nil returned by when will terminate the range construction
(when (< i limit)
(print ".")
(cons i (simple-range (inc i) limit)))))
;; prints 3 dots
(def rrr (rest (rest (rest (simple-range 1 10)))))
;; prints 4 dots
(def nnn (next (next (next (simple-range 1 10)))))
Quick questions on atoms — if I understood the thing correctly the way they should be used is by dereferencing them once at the beginning of where they're used — and not dereference them every single time that is needed, in order to have the same value for the entire time of the computation.
…because dereferencing it every single time that is needed might ultimately give two different values
that's correct, they're mutable, so they can change from under you.
but if I dereference them once, at the beginning of my computation and save the value using a let binding
I am "protected" — the box can point to something else, the value I dereferenced is immutable
if you store an immutable value in the atom, yes
Ok perfect, that makes completely sense. Thanks @borkdude
@vincenz.chianese Note that for updating atoms you generally want to use swap!
, if the update function references the current value
e.g. don't do this:
(def x (atom 1))
(reset! x (inc @x))
but do this instead:
(swap! x inc)
@borkdude Oh yeah for sure, I think I've understood the semantic of swap! and reset!
I was just having some doubts on how the mechanism would make sure that there are no race conditions and weird stuff in multithreads; now that I understood the thing (separate the value from its pointer) it's totally clear.
Hey there.
I am quite new to Clojure and I cannot really grasp why why str/join
is ignoring the last argument here. bb '(-> "some str" (#(str/split %1 #" ")) (str/join "-"))'
I understand there are two signatures for clojure.string/join
function but I can’t get why the extra argument is ignored here. Using macro for anonymous function of course solves the issue, but shouldn’t this work like this, too. If not - why not?
1. that should be in #beginners 2. that should probably be ->>
(`bb '(->> "some str" (#(str/split %1 #" ")) (str/join "-"))'` )
Yeah, thanks.
The ->
macro is going to to thread the return values through in the first argument position. The line you have there ends up compiling to: (str/join (#(str/split %1 #" ") "some str") "-")
. I think what you'd like is something like this: (str/join "-" (str/split "some str" #" "))
.
Does that clear things up?
As you're new to the language, one of the things worth knowing about Clojure is that many of the core functions take a "garbage in, garbage out" approach. So even though your original form evaluates to "-", the call itself has the arguments out-of-order – (str/join ["some" "str"] "-")
Yeah, I was sure I was using thread-last macro, many thanks. 🙂
Is it possible to use System/getEnv in edn file?
not natively as such but we use https://github.com/juxt/aero which adds some extra tags for them
Ok great. Do you know whether it's possible to use clj with different deps.edn names? Like deps.development.edn deps.production.edn etc?
Aliases are usually used for that...
E.g. you have :dev alias with extra paths and deps
Perfect! Thank you!
@ad399 When I'm trying to figure stuff out I usually use the macroexpand-all
function from clojure.walk
to figure out what is the final form. This should be helpful to debug more issues in the future:
(use 'clojure.walk)
(macroexpand-all '(-> "some str" (#(str/split %1 #" ")) (str/join "-"))
This will return
(str/join ((fn* [p1__9737#] (str/split p1__9737# #" ")) "some str") "-")
@mitesh This is very nice, thank you.
Are there any suggestions or improvements?
(defn take-sorted-by [c n f]
(let [m ^TreeMap (TreeMap.)
m-size (volatile! 0)
m-add (fn [k v] (if-let [a ^ArrayList (.get m k)]
(.add ^ArrayList a v)
(.put ^TreeMap m k (doto (ArrayList.) (.add v)))))
m-sub (fn [k] (let [a ^ArrayList (.get m k)
size ^int (.size a)]
(.remove a (dec size)) ;; (dec size) have to be int type!
(when (.isEmpty ^ArrayList a)
(.remove ^TreeMap m k))))]
(doseq [v c]
(let [k (f v)]
(if (< @m-size n)
(do
(m-add k v)
(vswap! m-size inc))
(let [max-key (.lastKey ^TreeMap m)]
(when (< k max-key)
(m-sub max-key)
(m-add k v))))))
(->> m vals (apply concat))))
(defn take-sorted-by-naive [c n f] (->> c (sort-by f) (take n)))
(let [c (range 100000000)
n 10
f #(rem % 15000000)]
;; (-> (take-sorted-by-naive c n f) prn time) bad idea, Execution error (OutOfMemoryError)
(-> (take-sorted-by c n f) prn time))
(0 15000000 30000000 45000000 60000000 75000000 90000000 1 15000001 30000001)
"Elapsed time: 2268.525789 msecs"
yep, ofc (:import [java.util TreeMap ArrayList])
In cases where you need to accumulate a result across a sequence, and check for an end condition after each new item, are there any general guidelines on when to prefer loop/recur vs reduce/reduced? It seems like it might be best to prefer reduce if that is sufficient, since it's higher level. But on the other hand I haven't seen that as often and there might be more nuance involved there?
Here’s a nice little write up on it https://groups.google.com/g/clojure/c/Q9KrgqyToIo/m/0MqKg2WIAwAJ
I tend to use reduce
when there's just one accumulator of sorts. loop
for everything else that can't be expressed with other higher-order functions.
2 accumulators = one accumulator 🙂
within a given namespace, is it possible to see which vars aren't defined in it? i mean, which vars are defined elsewhere and required/imported in?
i have a habit of writing :refer :all
, and i want to step through my project's namespaces and convert those :refer :all
lines into :refer [specific-var]
lines
and it's a slow process to change it to be (:require ... [other.namespace])
and then compile and see what's failed, and then add them in one at a time
you might give https://github.com/technomancy/slamhound a try
One of my favorite books. Nice.
oh that's cool as heck
thank you so much
exactly what i was looking for
@nbtheduke clj-kondo can also help with this, provided you have linted all your sources so it can infer where vars are coming from in case of refer all
i should probably hop into #clj-kondo to ask, but it just says "use alias or :refer" for me. is there something specific I should do to make it figure it out for me?
Like I said, you should also lint the other namespace. Also you should have a .clj-kondo dir if you’re doing it from an editor, so it can save data there
okay, cool
Is there a way to get a collection's item by index and also remove it from the collection, all in one shot?
Essentially, something better than this:
Ideally I would like both
The new collection and the extracted item
vectors aren’t great for that type of thing, depending on what you’re doing maybe you could use an ArrayList instead?
Well essentially I'd like something like const [elem, newVector] = removeElement(originalVector, index);
I'm new to Clojure and my knowledge of Java is literally -1
not a problem! if you can provide some more context I might be able to point you in the right direction
Well again, I'm looking for a built-in function (if any) to do this:
user=> (remove-collection-index [9 8 7 6 5 4 3 2 1 0] 3)
[6 (9 8 7 5 4 3 2 1 0)]
there isn't a function to do this, and I would encourage you to structure your data in a way that you don't need it
agree, that’s why I was asking for more context 🙂
All right! Thanks for the help. I'm trying to get some proficiency in Clojure and I'm trying to implement the "Game of Pure Strategy" game.
In the game, there's a step where I need to draw an element from a deck and then remove it (the removal is part of the construction of the "next state")
This is the initialState I start with and then I have a function gameStep
that takes such state and provides the new one
I need to draw a card from all the decks, do a comparison and then call the gameStep
again (which has a guard clause of course, and is (= 0 (count (:bountyDeck currentState)
If there'a better way — I'm all for it!
one idea from the top of my head is to model the deck as a map from index to card
then the “get and remove” would be as easy as calling (dissoc deck index)
{:1 1}
Something like this?
yeah
:thinking_face: I'm curios now, why is it ok for maps to get and remove and not so ok for vectors?
because maps support removal by key and vectors don't
Ah ok — so the idea is not that bad, I'm just using the bad data structure if I'm following
exactly :thumbsup:
but I'm on the same situation now?
I get a new map, but not the removed value — I'd still need to lookup the element value manually
do (get testMap :1)
and store that before you dissoc
Got it, and I'm assuming there's no way to do that in one shot with a built in function
Wouldn't a set be even better?
I don’t think so, no
a set is not ordered, I assume the deck is?
No, I do not care about the order
then set is better, yes! sets use disj
instead of dissoc
, fyi
Ok, let's then!
Ok I got it working. It's terrible, I know, but it's a start. I'll go from there, thanks!
(= 0 (county bountyDeck))
should be replaced with (empty? bountyDeck)
. Or just swap the branches in if
and replace the condition with (seq bountyDeck)
.
Unless, of course, boundyDeck
is some custom collection type.
When you want to get-in
a path of only keywords, you can just use the ->
macro: (-> current-state :firstPlayer :deck)
. Both shorter and faster.
update-in
with a path of a single item can be just update
.
(update-in [:bountyDeck] (disj drawn-card))
and the lines below it don't od anything - you should remove the parentheses around the call to disj
.
@p-himik Thanks for the suggestions — I do have a doubt about the last line indeed. I made a mistake in the copy paste
What I'm doing at the moment is (update-in [:secondPlayer :deck] #(disj % second-player-card))
Using an anonymous function, but if there's a way to avoid that, even better. I haven't yet found one.
Just use (update-in [:secondPlayer :deck] disj second-player-card)
update-in
internally does (apply f v args)
.
Ah ok I understand, this is because update-in
takes an additional array of parameters that will feed to the function after the main arugment
When in doubt, check the source code. :)
Didn't notice that
Ok so this is what I have now
LGTM
Glad to hear that!
I've also reversed the if order as suggested and avoided the new state symbol
One tiny thing - it's better to have a new line right after [current-state]
to avoid shifting everything to the right too much and creating too much empty space on the left.
looking good! :thumbsup: small nitpick/fyi: variables and keywords in clojure are usually `:snake-case` rather than `:camelCase`
@schmee I've changed them all 🙂
@p-himik Great suggestions, it looks way better now
It turns out I can use de-structuring on maps as well https://github.com/XVincentX/gops-clojure/commit/f1960a1157f16112b45c76513ec0fd05881d839d
I wouldn't recommend using maps-within-maps destructuring - it's much harder to read than a single level destructuring or a maps-within-vector destructuring.
I would maybe write it instead as
(defn game-step [{:keys [bounty-deck first-player second-player] :as current-state}]
...)
And then you don't really need to create first-player-deck
- just use (:deck first player)
right where it's needed.Makes sense — better not go too crazy I guess
First lesson learned: accessing nested maps can be a pain, so better keep data structures as simple as possible :troll:
c n f v k m
those are all pretty unhelpful variable names
looks good, definitely on the right track! :thumbsup:
@vincenz.chianese If you want to remove by index and get an item at the same time, you can always use juxt
(juxt :1 #(dissoc % :1))
will return a vector that is basically
[(:1 ds) (dissoc ds :1)]
when called on whatever structure
Oh interesting
juxt is generally pretty useful if you want to do things "all in one shot" (logically, there might be multiple passes over data)
I can see! Calls multiple functions on the same arg right?
(let [[value new-coll] ((juxt #(nth % 3) #(remove (partial = 3) %)) collection)])
and returns the result in a vector
That’s weird right?
user=> (map #(doto % println) [1 2 3])
(1
2
3
1 2 3)
What's weird about it?
Shouldn’t it be outputting?
1
2
3
(1 2 3)
Why?
The side effect should trigger before the result is returned for printing right?
map
produces a lazy seq, so the REPL starts to print a sequence (
then it has to realize the first chunk of it -- which causes the function to be called on three elements -- and then it can print that chunk.
When you mix laziness and side-effects, you should expect interleaving 🙂
Try (map #(doto % println) (range 40))
and you'll see chunking in action.
I suppose that makes sense. Sounds like I’m picking a terrible test case to showcase Clojure’s ability to map over vectors, lists, and hash-maps.
It'll print (
then 0 to 31 (from the println
) then the result of the first chunk -- a line with 0 1 2 3 .. 31
-- then it'll print 32 up to 39 (from the println
) then the next chunk of the result -- a line with 32 33 .. 39
and the )
I’ll just switch to a pure function
I’ll try it
Never mix side-effects and laziness!
If you use mapv
it is eager and you won't get interleaving.
(but, yes, a pure function would be a better demonstration -- otherwise you have to explain laziness and chunking!)
I’m surprised I haven’t run into that before, but thanks for explaining it.
Although I suppose I usually print the seq or use a do-seq for that