This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-02-26
Channels
- # announcements (18)
- # aws (17)
- # babashka (19)
- # beginners (141)
- # calva (73)
- # cider (4)
- # clj-kondo (13)
- # cljs-dev (2)
- # clojure (97)
- # clojure-europe (6)
- # clojure-italy (5)
- # clojure-nl (1)
- # clojure-spec (25)
- # clojure-sweden (2)
- # clojure-uk (25)
- # clojured (3)
- # clojurescript (63)
- # core-typed (6)
- # cursive (23)
- # data-science (4)
- # datomic (74)
- # fulcro (19)
- # graalvm (18)
- # graphql (3)
- # hoplon (63)
- # jackdaw (1)
- # juxt (23)
- # london-clojurians (3)
- # meander (7)
- # off-topic (23)
- # om (1)
- # pathom (13)
- # pedestal (2)
- # perun (2)
- # re-frame (38)
- # reagent (3)
- # reitit (24)
- # shadow-cljs (91)
- # spacemacs (14)
- # sql (4)
- # tools-deps (8)
- # vim (3)
Hi folks, I would like to convert something like this to a string: From this
({"Question1" "Text1"}
{"Question2" "Text2"}
{"Question3" "Text3"})
To this:
“Question1- Text1
Question2 - Text2
Question3 - Text3”
Thank youPerhaps a reduce. You join the k
and the v
with a "-" and join that with the accumulator which would be an empty string to start.
one approach
(->> [{"Question1" "Text1"}
{"Question2" "Text2"}
{"Question3" "Text3"}]
(map (comp (partial string/join " - ")
(partial apply concat)))
(string/join \newline))
sending that through println gives
Question1 - Text1
Question2 - Text2
Question3 - Text3
The map is inside ( )
map doesn't care
it treats a vector and a list the same way, and vectors are more convenient for literals
try it
oh this also needs (require '[clojure.string :as string])
can't assume too much in #beginners :D
I will try that. Thank you
one slightly odd thing is the usage of one key maps - since in clojure maps aren't ordered, so this would print more keys if present, but the order would not be guaranteed for the individual maps
(reduce #(let [[k v] (first %2)]
(str %1 k " - " v "\n"))
""
[{"question1" "text1"}
{"question2" "text2"}
{"question3" "text3"}])
Came up with thishere's a version imitating string/join and using StringBuilder
(let [sb (StringBuilder.)]
(doseq [m [{"Question1" "Text1"}
{"Question2" "Text2"}
{"Question3" "Text3"}]
[k v] m]
(.append sb k)
(.append sb " - ")
(.append sb v)
(.append sb \newline))
(str sb))
I like this solution because using StringBuilder is efficient. So I tried it with transducers, since I'm trying to learn how to use them.
(let [coll [{"Question1" "Text1"}
{"Question2" "Text2"}
{"Question3" "Text3"}]]
(print
(transduce
(mapcat (fn [m]
(let [[k v] (first m)]
[k " - " v \newline])))
(fn
([sb] (str sb))
([sb x] (.append sb x)))
(StringBuilder.)
coll)))
I was surprised how well it worked out to create the StringBuilder and convert it to a string, all within the transduce.Even better, the reducing fn can create the StringBuilder and be broken out as a reusable fn.
(defn build-str
([] (StringBuilder.))
([sb] (str sb))
([sb x] (.append sb x)))
(let [coll [{"Question1" "Text1"}
{"Question2" "Text2"}
{"Question3" "Text3"}]]
(print
(transduce
(mapcat (fn [m]
(let [[k v] (first m)]
[k " - " v \newline])))
build-str
coll)))
I think I'm in love with transducers...@orlandomr27 probably the more important thing is to explore clojure's various built in functions for iterating and consuming collections - they are weird if you haven't used fp before but they help make elegant code (and code with less places for bugs to hide)
reduce, map, string/join, and doseq all specialize different tasks that consume an input one element at a time in order
The first solution worked just fine for me @noisesmith. I’m struggling to understand partial but I’ll get there cause I’m new to FP
oh yeah I threw comp and partial in there haha
this might help
user=> (def my-inc (partial + 2))
#'user/my-inc
user=> (my-inc 2)
4
It helps! Thank you
and (comp f g)
returns a function that is effectively (fn [x] (f (g x)))
- if g takes one arg
user=> (def bump (partial + 10))
#'user/bump
user=> (def triple (partial * 3))
#'user/triple
user=> ((comp triple bump) 5)
45
user=> ((comp bump triple) 5)
25
Nice to know this solution, thanks!
it's the same as the dot operator in algebra for function composition
(except we don't do infix)
Nice explanation. I will save this messages as favorites. Everything is clear when you break it in parts and use the repl to evaluate each part
Thank you @noisesmith
Hello, some link or books for learning more about Clojure? I read Clojure: Brave and True is an awesome resource
Here are some books: https://clojure.org/community/books
I've been reading Programming Clojure, 3rd Edition - really good, also Elements of Clojure and Living Clojure.
Can you refer to an atom you expect to be in the namespace in a macro? I have tried the following:
(def rules (atom {}))
(defmacro rule [rulename rulebody]
(list swap! assoc rules rulename rulebody))
by quoting it (list swap! 'rules assoc rulename rulebody)
. you don't need macros at all for this particular case though, a function would suffice
Thanks! I had tried that, but included other errors in my code which made me think the ' was mistaken. It turned out to be st else 🙂
I was curious if anyone could point me to an example or help me understand how to setup retit-ring routes with ring middleware.
(def router
(ring/router
[
["/ping" {:get ping-handler}]
["/echo" {:get echo-handler
:middleware [[wrap-json-params] [wrap-keyword-params] [wrap-json-params]]}]]
))
@scott.archer check out the example at https://github.com/metosin/reitit/blob/master/examples/ring-example/src/example/server.clj - there are a bunch of examples in that repository but I found it pretty hard to follow some of them when I was getting started with it. Are you trying to get middleware on only a specific route or just trying to get middleware working for the first time?
I had a working "echo handler" in ring, but when I added retit, it's now broken. My ping / pong route works though.
@scott.archer Based on the documentation at https://metosin.github.io/reitit/ring/ring.html , your code looks correct to me - maybe the problem is somewhere else in your reitit configuration?
@scott.archer You have wrap-json-params
in there twice, maybe they’re fighting each other?
is there an easy way to merge/convert a list
of key-val pairs ('micromaps') to a big map? all keys are unique
if you had a list of actual (micro)maps that would work, but not for a list of keyval pairs
i'm just trying to go through http://exercism.io , sometimes writing noob code and reinventing the wheel... this is the task btw - https://exercism.io/tracks/clojure/exercises/etl/solutions/3344fc53e309435284f45285b752da24
For context I am starting a prepl in a docker container and would also like to pass it some init file, but I also have that file available in my application for a local dev testing.
~/C/clj-scratch $ ls src/bfabry/ 09:48:09
hello.clj scratch.clj
~/C/clj-scratch $ clj 09:48:15
Clojure 1.10.1
user=> (require '[ :as io])
nil
user=> (slurp (io/resource "src/bfabry/hello.clj"))
Execution error (IllegalArgumentException) at user/eval3 (REPL:1).
Cannot open <nil> as a Reader.
user=> (slurp (io/resource "bfabry/hello.clj"))
"(ns bfabry.hello)\n\n(defn -main [& args]\n (println \"Hello, world\"))\n"
user=>
I am trying to pull in a regex into a defn as a parameter, but having problems. Can someone please point me to the proper syntax? Right now I have something like this, but “no worky”:
(defn checker? [s re]
(re-find #re s)
)
(checker? "[a-m]" "b")
ClassCastException java.lang.String cannot be cast to java.util.regex.Pattern clojure.core/re-matcher (core.clj:4667)
Is there any way to make that string I’m trying to pass in to be interpreted as a regex?Ah, cool. I just found it! http://clojuredocs.org/clojure.core/re-pattern
I have a question but I am really bad at wording things so hopefully this makes sense. Say I am running a web app that uses a Java object that is not thread safe (Doesn't support multithreading). The object is referenced by id, say :id => 123
and we can perform actions on that Object. If two requests come in and they both reference the same object 123
. Would this be the same thing as multithreading? Would they both be accessing the same object? Does each request run in isolation? Does my question make sense?
it depends on what framework/library you're using to serve requests, but in general yes a lot of them have multiple threads for serving
with ring / jetty yes each request is in its own thread
you need some other mechanism to prevent concurrent access
never use mutable apis on objects inside atoms, this will break
atoms don't lock your data, they retry if there's concurrent change, and if your operation happens to mutate an object, that mutation will be redone
agents have the right semantics (only one thread can operate on an agent at a time), though they are weird in other ways
you tell me - if the object is at namespace or global scope, yes, all the requests will use the same object
Okay I thought so but I was told that each request was in isolation so I wasn't sure so wanted to ask.
Two requests in ring/jetty are effectively each running on a different thread and thus yes, it is multi-threaded.
So if two requests were to access the same instance of an object concurrently, where the object is not thread safe, this could cause bugs
@UB0S1GF47 each request is in its own thread, but threads are not isolated
We have something like (def workbooks (atom {})
and we load a workbook from a DB and store it within the atom with (swap! workbooks [wb-name wb-version] {:bit-stream wb-bitstream})
If its a new one per request then its fine. Each request execute within the same thread in a single threaded way (unless you yourself multi-thread it some more).
What noisesmith was saying about atom is that, if you mutate the mutable objects as part of a call to swap!
it can cause bugs.
In theory, if you make non idempotent modification in swap! in a single threaded context, that is also fine, but its kind of bad practice.
And it can help to understand why. Basically, if two things try to swap! simultaneously the same atom (which can only happen in a concurrent scenario), one of them can execute the swap!, but fail to commit the result, and will thus retry the swap! a second time, and a third, etc., until it succeeds.
Because the swap! can be retried, you need to make sure that whatever you do in swap! can be retried. Only idempotent operations are safe to retry.
So say you read the next entry in the bitstream inside your swap!, you can imagine that this isn't safe to retry, since every time you read the next entry you get a different bit
@U0K064KQV Thanks for this, explanation helps!
It's not commonly used in clojure, but the locking
macro is an option for situations like these.
Its hard to accidentally do in pure Clojure, but if using mutable Java objects it can slip up
In pure Clojure you might have:
(def a
(atom
(atom 0)))
(swap! a #(reset! % (inc @%)))
Ok, thanks. So the most common source of danger is Java interop / mutable datastructures.
no, I would consider attempting to do so a bug
Atoms in atom is definitly weird and a bit of an anti-pattern. Any mixing of mutable and immutable kind of is
If you end up in a place like that, and you really really need it, I can only think of performance for why that would be, you might want to just go full mutable and replace your atom by a mutable Java or JS collection instead
Thanks all, this is what I wanted to hear, which is basically that non-idempotent atom swaps are really not a thing, so long as you're doing pure Clojure. Granted that Java interop isn't uncommon at all, but, anywhere I'm doing that, I already have my guard up, so to speak.
Even thinking of putting Java arrays inside of an atom gives me a queasy feeling. I suppose I can imagine legit uses for it.
That be an anti-pattern too mostly. Atom expects you to store immutable things inside it, and swap! expects your swap function to be idempotent.
on the other hand, a mutable object inside an agent
so that only on thread messes with it at a time is a useful pattern
Does this mean that it will block the other threads? For example, say 10 requests try accessing an object inside the agent does this mean 9 req are on standby until one-by-one get access to the agent
?
yes - for writes - reads still won't block
also this requires using the agent correctly, it doesn't prevent doing something stupid like deref and modify - just makes it easier to do the right thing
Agents are async, so not really, but the changes won't necessarily reflect immediatly. You can use await to block
oh right - the thread won't be blocked, but it can return before the operation on the agent does
await doesn't wait for the queue to be empty - it creates a function that flips a switch and returns identity to the agent, and waits for the switch to be flipped
which is a nice clue for a way to block until your action has completed
it isn't attached to a modification - it sets things up so your execution is resumed when the message await sends hits the agent
just read docs, I don't think I'm right about that...
Blocks the current thread (indefinitely!) until all actions
dispatched thus far, from this thread or agent, to the agent(s) have
occurred. Will block on failed agents. Will never return if
a failed agent is restarted with :clear-actions true or shutdown-agents was called.
so if you call it after your modification, await returns after your mod, but maybe after some other as well
Ya. Waits until all "actions dispatched thus far" --- thus far meaning the time of awaiting, not the time of modifying.
(cmd)user=> (def a (agent 0))
#'user/a
(cmd)user=> (do (future (dotimes [_ 1000] (Thread/sleep 100) (send a inc))) (await a) @a)
79
(ins)user=> (do (await a) @a)
167
(cmd)user=> (do (await a) @a)
388
the future is repeatedly sleeping for 100 ms then incrementing a
My understanding is you can send to the agent from the agent, thus if awaiting, you might never be done, if the agent sends things to itself
the way await works, if you read the source, is that it puts an action that counts down onto the agent, once that runs await returns
when the agent sends to itself, that would add to the end of the queue (after the special payload from await)
Hum.. interesting. So effectively it be, say the queue is at 130, and you await, it will wait until the next 130 modification run
Why does it say from this thread though? Does it exclude modifications queued up by other threads?
that's effectively it yeah - the source to await is easier to understand than its doc :P
you can if you know they are already sent to the queue... but yeah you can only await "all things currently queued"