This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-08
Channels
- # announcements (20)
- # aws (16)
- # babashka (63)
- # beginners (75)
- # calva (35)
- # cider (2)
- # clj-commons (5)
- # clj-kondo (2)
- # cljs-dev (1)
- # clojure (90)
- # clojure-australia (3)
- # clojure-europe (16)
- # clojure-france (1)
- # clojure-nl (4)
- # clojure-uk (5)
- # clojurescript (7)
- # data-science (2)
- # datahike (1)
- # datomic (39)
- # emacs (31)
- # events (2)
- # figwheel-main (1)
- # fulcro (15)
- # gratitude (8)
- # helix (17)
- # holy-lambda (1)
- # introduce-yourself (1)
- # jobs (3)
- # kaocha (2)
- # liquid (1)
- # malli (1)
- # nrepl (2)
- # other-languages (1)
- # portal (76)
- # react (19)
- # reagent (9)
- # remote-jobs (1)
- # rewrite-clj (9)
- # shadow-cljs (31)
- # tools-deps (5)
- # xtdb (11)
When having the function below, is there any guarantee on the order the functions are executed? (Or are they even getting executed simultaneously?)
(defn test-func []
{:content {:cat-a (func-a args)
:cat-b (func-b args)
:cat-c (func-c args)}})
There's no guarantee for map literals, even though it appears to be ordered for maps fewer than 8 elements due to ArrayMap implementation details. Try evaluating the following:
{1 (#(println 1))
2 (#(println 2))
3 (#(println 3))
4 (#(println 4))
5 (#(println 5))
6 (#(println 6))
7 (#(println 7))
8 (#(println 8))
9 (#(println 9))}
If you require a certain execution order, try constructing one using the hash-map
function
Ok so don't rely on the evaluation order of maps and if it's important use hash-maps, got it. Thank you.
{}
creates hash-maps too, the difference is that the order that the args to the hash-map
function are evaluated is defined
Is there a function that prints the argument and returns it? From cl I'm used to put print
around something I want to log
Can loading a clojure file have side effects? I’m assuming calling clojure.core.load-file
with untrusted content is unsafe without running in a security context or otherwise doing a bunch of validation to check?
sure it can have side effects. load-file
evaluates its content so things like defmethod
or alter-var-root
can change the context
I assumed so, thanks.
Well, way more directly than defmethod
or alter-var-root
are things like creating connections and sending requests to servers, creating/deleting/renaming files, etc. executing arbitrary code.
In the past when working in a compile time type-checked language on the JVM I’ve used a restricted security manager to prevent I/O when evaluating untrusted code that’s meant to be pure - see https://gist.github.com/Mahoney/8979e520b7477c9a9771fe3e1da3472a#file-ktsobjectloader-kt-L62-L70 . But I don’t know whether that would stop someone dynamically altering Clojure in a way that would allow them to inject dodgy code into some function in another namespace that would then be called outside the security manager’s context… guess some experimenting might be in order.
this is a hard problem generally - there's some prior art in the clojurebot
and lazybot
tools which each provided an IRC sandbox clojure evaluation environment
and yes, many (most?) of the security patches required were related to changing things in the clojure compiler or jvm core libs in order to change the behavior of future code
(defn fetch-all-configs []
(let [
country-codes ["ID" "SG"]
config-names [{:key "conf1" :value "conf1-mapping" }
{:key "conf2" :value "conf2-mapping"}
{:key "conf3" :value "conf3-mapping"}]
all-configs []
(for [country-code country-codes
config-name config-names]
(reduce all-configs (reduce #(flatten-configs %1 %2 (:key config-name) country-code) [] (get-full-config-by-config-name country-code (:value config-name) )))
all-configs)
))
I see wrong for
using. It expects two arguments but there is three
(defn fetch-all-configs []
(let [
country-codes ["ID" "SG"]
config-names [{:key "conf1" :value "conf1-mapping" }
{:key "conf2" :value "conf2-mapping"}
{:key "conf3" :value "conf3-mapping"}]
all-configs []
(for [country-code country-codes
config-name config-names]
(into all-configs (reduce #(flatten-configs %1 %2 (:key config-name) country-code) [] (get-full-config-by-config-name country-code (:value config-name) ))))]
all-configs)
)
will into append every response of (reduce #(flatten-configs %1 %2 (:key config-name) country-code) [] (get-full-config-by-config-name country-code (:value config-name) ))
to all-configs
looks like you expect into to mutate all-configs but it is immutable operation. it just returns new collection
(defn fetch-all-configs []
(let [country-codes ["ID" "SG"]
config-names [{:key "conf1" :value "conf1-mapping" }
{:key "conf2" :value "conf2-mapping"}
{:key "conf3" :value "conf3-mapping"}]]
(apply concat (for [country-code country-codes
config-name config-names]
(reduce #(flatten-configs %1 %2 (:key config-name) country-code) [] (get-full-config-by-config-name country-code (:value config-name)))))))
for
returns a list of lists. and then apply concat
will combine those lists into one(apply concat '((1 2 3) (4 5 6))) ;; => '(1 2 3 4 5 6)
I dont’ know much your code to answer that but here ^ is how concat works.Want to concat the result of (reduce #(flatten-configs %1 %2 (:key config-name) country-code) [] (get-full-config-by-config-name country-code (:value config-name) ))
for each country-code and config-name pair in. all-configs list
Is there a better way to to do this?
(defn add-status-to-metadata! [status]
(let [object-metadata (ObjectMetadata.)]
(.addUserMetadata object-metadata "status" status)
object-metadata))
I use a java library to add some user metadata to an s3 object. I am trying create a new metadata object, add an entry to it and then return the metadata object with the status added (I believe its stored in a hashmap). This just seems weird to do in clojure. Is this really the way to go?(defn add-status-to-metadata! [status]
(doto (ObjectMetadata.)
(.addUserMetadata "status" status)))
yep, but doto
makes it feel less weird
(doto (ObjectMetadata.)
(.addUserMetadata "status" status))
anytime you're doing Java interop with side-effecting functions, it's likely a good choice
it also highlights how weird side effecting functions on stateful objects really is :)
Hi 👋 I made this simple SPA with re-frame, material-ui, and firebase. I'm new to clojure and clojurescript so if somebody want to take a look at the code, I'm open to recibe recommendations, better way to achive the solution, etc. Repo: https://github.com/vafer11/expenses Thanks.
I am not experienced enough to give you any coding tips, but I did find a misspelling on the home page 🙂 — Don't have and account? Sign Up
and
should be an
there
Question: I'm working on a Sudoku Solver (highly recommend as a start project, I'm learning a lot). Since I'm using an atom for the "board", is it more appropriate to, when writing functions, to pass the "board" through the front of the function or write my function as if "board" is guaranteed?
I'd go with pure functions (board-state as input, board-state as output). That way the atom is an "implementation detail" of the calling function.
definitely ^^
^ Also makes it so that you’re only derefing the atom once per iteration, so if you compute a bunch of values off of it they’re all consistent with each other and only suffer the overhead of a single deref.
in general, try to isolate the places where you touch state to as few places as possible - so only in the main loop that knows there is state (not in every function that touches the board)
Although, if it’s a solver and not an interactive game, I’m curious why it’s stateful at all.
sometimes doing recursive algorithms over trees are easier to write if you hold state on the side (even if non-concurrent / interactive!)
I appreciate the responses ... And I can tell I still have a lot to learn. I'm currently using an atom, so that I can watch my states carefully, and verify my "layers" of logic are working. Its possible when I'm done I'll realize I didn't need the crutch. I am leaving open the opportunity for converting this to an interactive. But not ready to open up that can of worms just yet. Currently just trying to get an easy puzzle solved.
(require '[clojure.core.async :as a :refer [chan to-chan pipeline-blocking <!!]])
(defn blocking-operation [arg])
(let [concurrent 10
output-chan (chan)
input-coll (range 0 1000)]
(pipeline-blocking concurrent
output-chan
(map blocking-operation)
(to-chan input-coll))
(<!! (a/into [] output-chan)))
=> errs
Exception in thread "async-thread-macro-3" java.lang.AssertionError: Assert failed: (not (nil? itm))
at clojure.core.async.impl.protocols$add_BANG_.invokeStatic(protocols.clj:40)
at clojure.core.async.impl.protocols$add_BANG_.invoke(protocols.clj:37)
at clojure.core$map$fn__5883$fn__5884.invoke(core.clj:2746)
at clojure.core.async.impl.channels$chan$fn__6185.invoke(channels.clj:300)
at clojure.core.async.impl.channels.ManyToManyChannel.put_BANG_(channels.clj:143)
at clojure.core.async$fn__12343.invokeStatic(async.clj:172)
at clojure.core.async$fn__12343.invoke(async.clj:164)
at clojure.core.async$pipeline_STAR_$process__12547.invoke(async.clj:534)
at clojure.core.async$pipeline_STAR_$fn__12559.invoke(async.clj:549)
at clojure.core.async$thread_call$fn__12462.invoke(async.clj:484)
at clojure.lang.AFn.run(AFn.java:22)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:831)
blocking-operation returns a nil
you can't put nil on channels
I see that explains it https://clojuredocs.org/clojure.core.async/pipeline-blocking this example needs to be fixed
I think you can do so on clojuredocs if you log in
I need to loop over a seq, generate some new values for each item, create some side-effects, and then return the set of generated values. What's the best way to do that?
or potentially, transduce
if you can break up your computation into steps that look like map
, filter
, partition-all
, cat
, etc.
It might be worth seeing if you’re able to separate the sideeffecting stuff from the pure stuff.
As in, rather than having one big reduce which does a bunch of calculations and some sideeffects, try to split it up in a pipeline like:
(->> some-data (map interesting-transform) (filter garbage?) (reduce persist! some-initial-value))
https://clojuredocs.org/clojure.core/doall might also be of interest
if you’re not using the return value from your sideffecting stuff, you could also consider using a doseq
or run!
to perform the sideffecting stuff.
Basically in this case I'm starting with a set of values, combining those values into a new set of maps with some computed values, and storing each map in my backend db. I suppose I could separate the map generation from the saving thereof.
And while doing that, have a look at this post about redundant fns when using map
et al. https://stuartsierra.com/2015/08/10/clojure-donts-redundant-map
Those Clojure Do's and Don'ts articles are a goldmine. Thanks for the link, haven't read that particular one.
That's pretty straight-forward advice there. I think at this point I understand many of those idioms, but lack the familiarity of choosing the right abstraction function when dealings with collections. Map, reduce, filter are the general tools that can do just about everything, but I'm finding I often come across abstraction functions that cover the stuff I implement myself with those basic fns.
I went with a let
instead of threading it. Any pointers for improving this, or does it seem okay?
(let [lines (-> files first :tempfile slurp (string/split #"\n"))
notes (map (fn [line] {:note/id (UUID/randomUUID)
:note/text line
:note/note-list {:note-list/id :singleton}})
lines)
ids (mapv #(select-keys % [:note/id]) notes)
res {:note-list/notes ids}]
(doseq [note notes]
(m/add-note! env note))
res))
Hi, I am trying to use built-in leiningen in IntelliJ Cursive, but I am not successful and need to use leiningen from CLI. Is the leiningen which is built-in into Cursive fully usable? For example I didn't find a way how to execute lein cljsbuild once
cljsbuild is not in the list of tasks in the leinigen window and I don't see any way to add it.