This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-03
Channels
- # babashka (17)
- # beginners (166)
- # calva (97)
- # cider (4)
- # clara (2)
- # clj-kondo (46)
- # cljsrn (5)
- # clojure (334)
- # clojure-canada (1)
- # clojure-dev (144)
- # clojure-europe (14)
- # clojure-germany (5)
- # clojure-nl (10)
- # clojure-spec (1)
- # clojure-uk (46)
- # clojurescript (50)
- # conjure (1)
- # core-async (52)
- # core-typed (5)
- # cursive (3)
- # datomic (3)
- # emacs (11)
- # figwheel (16)
- # figwheel-main (9)
- # fulcro (29)
- # graalvm (19)
- # graphql (14)
- # helix (46)
- # hoplon (4)
- # hugsql (2)
- # jobs (2)
- # jobs-discuss (1)
- # juxt (15)
- # kaocha (6)
- # off-topic (9)
- # pedestal (7)
- # portkey (7)
- # re-frame (10)
- # reagent (29)
- # shadow-cljs (13)
- # spacemacs (70)
- # sql (13)
- # tools-deps (26)
- # xtdb (23)
Can anyone tell me why this macro is working? From http://braveclojure.com and my understanding of macros this should not be working but it does. https://github.com/michaelwhitford/clojure-swapi/blob/master/src/swapi/core.clj#L48
(defmacro create-queries!
"Create 3 new functions for one action of the api
`action` required: config set"
[action]
`(do (def ~(symbol (str action "!")) (partial query! 'action))
(def ~(symbol (str action "+")) (partial query+ pool 'action))
(def ~(symbol (str action "-schema!")) (partial (str 'action "!") 'action "schema"))))
So, you can execute code at compile time without quoting. But in your case, def is a special form, and it takes a symbol, which is why you need to return a quoted version of the code where def is then given a symbol which is resolved at compile time
Thank you for that but it still works but I cannot expand the macro, and it has a bug in that species does not get "-schema!" appended, only "-schema" and since I cannot expand it at the repl I can't see what it is doing...
what does your attempt to expand the macro look like?
this is what I see locally
user=> (pprint (macroexpand '(create-queries! foo)))
(do
(def foo! (clojure.core/partial user/query! 'user/action))
(def foo+ (clojure.core/partial user/query+ user/pool 'user/action))
(def
foo-schema!
(clojure.core/partial
(clojure.core/str 'user/action "!")
'user/action
"schema")))
the nice thing about macro development is I don't even need query+ or pool or action to exist
that macro-expand exposes at least one bug in the macro - user/action! is under-evaluated, probably missing a ~
Ya, I don't know why your macro works in your code. When I copy/paste your macro as is, I get a compile error
Yeah me either and I can't figure out how to get macroexpand to work, it never shows me an expanded macro, just the call.
swapi.core=> (macroexpand '(create-queries! "planets"))
(create-queries! "planets")
swapi.core=> (ns-interns *ns*)
{films-schema! #'swapi.core/films-schema!, planets! #'swapi.core/planets!, options #'swapi.core/options, species! #'swapi.core/species!, people! #'swapi.core/people!, vehicles-schema! #'swapi.core/vehicles-schema!, p #'swapi.core/p, a #'swapi.core/a, planets-schema! #'swapi.core/planets-schema!, species-schema #'swapi.core/species-schema, pool #'swapi.core/pool, people+ #'swapi.core/people+, reset #'swapi.core/reset, starships! #'swapi.core/starships!, starships-schema! #'swapi.core/starships-schema!, people-schema! #'swapi.core/people-schema!, vehicles! #'swapi.core/vehicles!, species+ #'swapi.core/species+, films+ #'swapi.core/films+, films! #'swapi.core/films!, planets+ #'swapi.core/planets+, vehicles+ #'swapi.core/vehicles+, url #'swapi.core/url, query! #'swapi.core/query!, query+ #'swapi.core/query+, starships+ #'swapi.core/starships+}
It is for sure creating the functions, except species drops the ! for some reason. It's just weird.
Thanks for looking at it anyways, I think for now I will go back to a function that generates the functions, that worked fine I just wanted to learn macros
I can show you how to make it work. But I do not know why the previous macro you had, where you were just doing inline (def (str ...)) without a quote or unquote would have not thrown an error for you
In trying to get better at well written Clojure, does anyone have some projects to point to for study?
You can look at projects under the org.clojure namespace. Plus, there are a few developers who have reached celebrity status, like weavejester. You can check out those repos as well.
Thanks guys, I appreciate it 🙂 And sorry for late reply, meetings ...
What is wrong with this line?
(api/mutes-users-create
creds :params {:screen_name "jack"})
This is the error I get:
Syntax error (NullPointerException) compiling at (src/..../core.clj:7:1).
null
It helps if you look at the full stacktrace, it could show you the exact line where the exception is first thrown
Also because it says compiling at... I think it might be from inside a macro, not sure
Hey everyone 👋 . Quick question. What resource (I would prefer a full course) would you suggest to someone who hasn’t done any Clojure before and wants to get up to speed? I’m not new to functional programming, so I’m looking for a course that is a bit more advanced than “what are variables, what are functions …“. Preferably, the resource would be a full (web) application created in clojure + clojurescript, with practical examples of dependency management, project setup, deploying, the language specifics, such as the macro system, etc. Does something like this exist? I prefer to learn by doing (building) but I do appreciate the theory behind topics as well.
I'm a bit in the same situation as you describe, having experience from elsewhere. Not really a course, but I've been working through https://www.braveclojure.com/clojure-for-the-brave-and-true/
I was in the exact same position. I found most of my pain was learning new tooling (emacs for the first time) and learning how things are done. I used Clojure for the Brave and True to launch me into actually writing Clojure https://www.braveclojure.com/
Same here about tooling. I ended up picking Emacs/Cider and Boot, for example.
At the end of one of the earlier chapters it says "now go and write some Clojure" which I did and haven't actually returned back to the book (but will do at some point)
> which I did and haven’t actually returned back to the book Hahah, developers and taking things literally. Name a more iconic duo! I love it 😄
Thanks so much for the suggestions. So emacs is the weapon of choice when writing Clojure? How does VSCode stack up? I don’t like VSCode, but if it allows me to keep my processes unified I’d prefer to stay…
You know when books say "We really want you to do try our examples?" and you never do? First time I've ever listened 😆
I used to use Sublime, but it doesn't cut it for REPL work, and that's a big deal for quick iterative development so I went exploring
I first tried VSCode with it's emacs plugin but then I realised the ctrl+casdf shortcut to use it was a pain and I might as well be learning emacs if I want to learn a new big tool
So I'd say, do whatever is closest what you know and minimises the learning time to get you going
Emacs has a learning curve. But once I learned Emacs a few years back, there's not really anything like it. But, I'm a Emacs/Slime/Common Lisp guy too 🙂
> Preferably, the resource would be a full (web) application created in clojure + clojurescript this book is the gold standard https://pragprog.com/book/dswdcloj3/web-development-with-clojure-third-edition
Thanks again for all the resources, guys! The Web development with Clojure book and Brave clojure both look really nice!
You can also look for libraries to use here: https://www.clojure-toolbox.com/
Hello all, I know my way is the hard way to join, but is there any easy way to
[{:dia "2020-05-18" :total 0} {:dia "2020-05-19" :total 0} {:dia "2020-05-20" :total 0} {:dia "2020-05-21" :total 0} {:dia "2020-05-22" :total 0} {:dia "2020-05-23" :total 0}]
and
{:dia "2020-05-20" :total 1}
to be [{:dia "2020-05-18" :total 0} {:dia "2020-05-19" :total 0} {:dia "2020-05-20" :total 0} {:dia "2020-05-21" :total 1} {:dia "2020-05-22" :total 0} {:dia "2020-05-23" :total 0}]
?
IMO this data structure works much better as a map:
{"2020-05-18 0
"2020-05-19" 0
"2020-05-20" 0
...}
then you can just do (assoc my-map "2020-05-20" 1)
or even (update my-map "2020-05-20" inc)
Is this example right? The second argument is about day 20, and the result gets day 21 incremented. If it is right, I don't get what you're trying to do. Do you mind explaining, please?
Anyway I seconded @UGRJKK74Y. That could be a much better data structure (except if you've simplified the actual map for the sake of the example).
even if it's simplified for the example, you can use reduce or group-by to turn the example into what @UGRJKK74Y suggests, and the change back would be simple as well
the right way to solve a clojure problem is usually to turn the data into the shape where the problem is easily solved
exactly. if this is the end format, i'd recommend a different format for processing then a transformation at the very end
Yes, nice tips. Thank you
user=> (pprint (denormalize (update (normalize account-vec)
"2020-05-21" inc)))
({:dia "2020-05-18", :total 0}
{:dia "2020-05-19", :total 0}
{:dia "2020-05-20", :total 0}
{:dia "2020-05-21", :total 1}
{:dia "2020-05-22", :total 0}
{:dia "2020-05-23", :total 0})
(defn denormalize
[account-map]
(sort-by :dia
(into []
(map (fn [[k v]]
{:dia k :total v}))
account-map)))
(defn normalize
[account-vec]
(into {}
(map (fn [{:keys [dia total]}]
[dia total]))
account-vec))
also, the sanity test:
user=> (= account-vec (denormalize (normalize account-vec)))
true
Is it ok to do all this transformation to update one field from performance perspective?
if you're gonna do somethingl ike that the the normalize
step would happen once when you first get the data and the denormalize
step would happen right before you send it off to wherever needs it
And if this increment is just a small step inside a bigger processing (using other fields of the original map) that must be performed many times?
Sorry for playing the devil's advocate here, I really would like to fully understand it
@UNZQ84WJV I would typically try to arrange things so those conversions happen as few times as possible, but even for a single changed row, the amount of work done isn't so much more than what it would take to filter / replace / reassemble the vector as is
(where there are variables like the size of the input of course...)
the ideal case of course is changing the canonical record so you don't ever have to convert
I see. Yes, I agree that changing the original data structure would be the ideal solution. Thank you, @noisesmith and @UGRJKK74Y for the insights.
Learning how to spec (I have looked at just about many vids on youtube on spec and with my limited knowledge the examples are simple (to teach an understanding as I appreciate), however as soon as the structures are more complex there are no techniques/approaches provided - especially some of real data structures I am sure a pretty complex) I ultimately want to check that if the JSON conforms to the spec. In addition, it would be great to exercise the spec and generate data that is conformant following an API call the returned JSON looks like the following {:abc [{:loca {:addr {:country "USA"}} :dets {:some{:timestamp "2020-04-09T15:27:00" :Url "https://mywebpage.com/answer" :prod {:name "this product"}}} :totalNumber 399 :pieceID ["ABC1" "DEF2" "GHI3"]}]} ;; so for all the leaves I can create as below; (s/def ::country string?) (s/def ::addr (s/keys :req [::country])) (s/def ::loca (s/keys :req {::addr [::country]})) (s/def ::abc (s/keys :req {::loca{::addr [::country]}})) (s/conform ::country "USA") ;; => "USA" (s/conform ::addr {::country "USA"}) ;; => #:myproj.trial.spec{:country "USA"} (s/conform ::loca {::addr {::country "USA"}}) ;; => #:myproj.trial.spec{:addr #:myproj.trial.spec{:country "USA"}} (s/conform ::abc {::loca {::addr {::country "USA"}}}) ;; => #:myproj.trial.spec{:addr #:myproj.trial.spec{:country "USA"}} However this cannot be right as loca is in a vector in the JSON?? The s/def s that have been defined have no relation to the JSON yet ;; (etc ...............) (s/def ::totalNumber (and pos? int?)) (s/conform ::totalNumber 399) ;; => 399 (s/def ::pieceID vector?) (s/conform ::pieceID ["ABC1" "DEF2"]) ;; => ["ABC1" "DEF2"] Q1: Considering the JSON at :abc has a vector to be considered what is the sane approach? Do I s/def ::abc including a map function over all the vector elements and ensure each vector element is considered? Q2: In the example, :pieceID is a vector; therefore do I include the vector in the s/def (analogous to Q1) for :pieceID?
@UEGT2541J, it is very hard to understand code without proper formatting. Could you put it in a gist, please?
I deleted the message and will put up again with gist
Thank you!
Resubmitted to slack
Learning how to spec (I have looked at just about many vids on youtube on spec and with my limited knowledge the examples are simple (to teach an understanding as I appreciate), however as soon as the structures are more complex there are no techniques/approaches provided - especially some of real data structures I am sure a pretty complex) I ultimately want to check that if the JSON conforms to the spec. In addition, it would be great to exercise the spec and generate data that is conformant following an API call the returned JSON looks like the following <script src="https://gist.github.com/dev4openid/1becc5be61b764e330edc82d16f99eb6.js"></script> Q1: Considering the JSON at :abc has a vector to be considered what is the sane approach? Do I s/def ::abc including a map function over all the vector elements and ensure each vector element is considered? Q2: In the example, :pieceID is a vector; therefore do I include the vector in the s/def (analogous to Q1) for :pieceID?
The URL https://gist.github.com/dev4openid/1becc5be61b764e330edc82d16f99eb6.js is not JSON btw
yes - it was a JSON before I transformed it into a map 😉
output it?
Oh I see. Here's the URL I think you mean, now I can see the gist: https://gist.github.com/dev4openid/1becc5be61b764e330edc82d16f99eb6
so do i s/def a structure that has :loca :dets : totalNumber : pieceID and then map it over the vector while I am in the s/def of the :abc? Similarly do the same for the PieceID vector?
Is that the appropriate appoach?
Can you describe to me what should :abc be? The spec is different from the data structure at the top of the gist
What should each vector contain? A map of loca+dets+totalNumber+pieceID and nothing else? That's simple
:abc is just a keyword that is returned - nothing special about it
The :abc has a vector and the vector has only one intsance of data in it for now
req-un probably makes more sense: (s/def ::abc (s/coll-of (s/map-of :req-un [::loca ::dets ::totalNumber ::pieceID])))
Your (s/conform ::abc ...) example will stop working because you're passing it a map, not a vector
OK so by defining all the leaves as I sis - I think that is correct? Then I formulate the appropriate collections? (I refer to the PieceID.)
In this manner I build up the spec. Correct?
Ooops! "leaves as I spec" Fat fingers
I'm not sure. Bottom up from the leaves could work. I've been working top-down and putting in "any?" where needed
To conclude I build the specs for the lkeaves. For all vectors one would use s/coll-of for each vector until the top level. That way each element has a spec
To ensure coverage. I have been watching https://www.youtube.com/watch?v=fOv_z6E30l0 for example
Interesting he does it from the bottom up
To get familiar with the spec functions use https://clojure.org/api/cheatsheet and see the box where it says "Spec (https://clojure.org/about/spec, https://clojure.org/guides/spec)"
You can see all the spec functions that work on a collection at a glance: coll-of map-of every every-kv keys merge
(def ex
{:abc [{:loca {:addr {:country "USA"}}
:dets {:some{:timestamp "2020-04-09T15:27:00"
:Url ""
:prod {:name "this product"}}}
:totalNumber 399
:pieceID ["ABC1" "DEF2" "GHI3"]}]})
(s/def ::country string?)
(s/def ::addr (s/keys :req [::country]))
(s/def ::loca (s/keys :req {::addr [::country]}))
(s/def ::dets any?)
(s/def ::totalNumber int?)
(s/def ::pieceID (s/coll-of string?))
(s/def ::abc (s/coll-of (s/keys :req-un [::loca ::dets ::totalNumber ::pieceID])))
(s/def ::root (s/keys :req-un [::abc]))
(s/conform ::root ex)
OK, thank for your help. I will have to absorb and get on with it 😉 Cheers
btw for future questions, #clojure-spec is probably better
No problem 🙂 The REPL is your friend here for quick experimentation "oh that's how it works". I learnt all of this a week ago -- I'm sure you'll catch up quick
OK thx
I have a bunch of *.edn
files in a resource directory who’s specific names I do not know (or rather don’t want to have to know). What is the Clojure idiomatic way to list all of the files in a resource directory? I have tried (file-seq (io/file (io/resource "path/to/edn/resource/directory")))
. That works during local development but fails to load properly once the project has been compiled to an uberjar
.
right, resources are not files, and file apis don't work on them except accidentally
I do wish we had resource-seq - it wouldn't be hard to write, I might be able to pull up a snippet later if no-one beats me to it
I think it actually is hard to write in a totally generic way
fair - I think the common case is looking for a directory under the startup classpath (where resources ends up), I hope that simplifies it enough
even narrowing it to directories and jar files would suffice
I think it’s worth considering why this is hard and maybe why you shouldn’t do it
Certainly the op’s use case seems like a bad idea and it being hard is a signal worth listening to
That is wise advice. In the end, I created a single resource file that listed all of the resource files I wanted to load. This imposes the extra step on our dev team of having to update this file whenever a new .edn
resource is added (this is a GraphQL server for context) but the upside is that the list of files that is used to construct the schema is explicit. Additionally, the chance of forgetting to add the file is pretty low, assuming the server is simply run during local dev.
yeah, that is what I was looking for but could not find it. My experience matches exactly what you said. If I’m loading a specific file, everything works fine but it chokes on directories as they are not files
Hi guys. i want to add a value by one when ever it meet a condition inside for-loop. its directive way is something like:
for(i=0;i<5:i++){
if(some conditions){
count++}}
But as data is immutable in clojure i cant do count++. How can i apply that? i think i must use recur. But i cant figure out how. thanks in advanceYou can use reduce
for things like that.
(reduce (fn [counted i]
(if some-condition
(inc counted)
counted))
0
(range 5))
Thank you sir But what if i wanted to do it in nested loop? fn would only take 2 parameters and we need to add second loop param as well. imagine we want to count number of similar members of two [3*3] vectors like:
(defn reduce-test [x y]
(for [i (range 3)
j (range 3)]
(reduce (fn [counted i]
(if (= (get-in x [i j]) (get-in y [i j]))
(inc counted)
counted))
0
(range 9))))
this will not count properly cause we only evaluate i inside fnYou can use pairs of integers plus a little destructuring, like this:
(reduce
(fn [counted [i j]]
(let [in-x (get-in x [i j])
in-y (get-in y [i j])]
(if (= in-x in-y)
(inc counted)
counted)))
0
;; generate a list of pairs of integers
(for [i (range 3), j (range 3)]
[i j]))
This makes a nice toy example, but honestly I’d be surprised to see code like this in code I actually wrote. Clojure (and functional programming) are different enough from imperative programming that you can end up with weird looking code if you describe your problem in terms of “this is what I would do if I were writing a ‘for’ loop in python” (or whatever). If you describe the underlying problem you’re trying to solve, you might see a very different approach from experienced Clojure programmers.
sure.Thank u for valuable advice. Actually what i really wanted to do was to check number of same arguments inside nested vectors. So i used my imperative mind-set to settle it down. could u be kind enough to explain functional way of thinking through this problem?
Can you give me an example of some nested vectors you want to check?
[[1 2 3][4 6 5][7 8 9]] [[2 8 7][4 6 3][9 1 5]] so counted is 2 as we have 2 same args at same [i j]
Ok, straight matrix manipulations like this do lend themselves to a more imperative approach, but if I wanted to demonstrate a more functional approach, I might try something like this:
(def first-nested-vector [[1 2 3] [4 6 5] [7 8 9]])
(def second-nested-vector [[2 8 7] [4 6 3] [9 1 5]])
(defn count-matching-elements
[v1 v2]
(mapv (fn [a b]
(if (= a b) 1 0))
v1 v2))
(reduce + (mapcat count-matching-elements first-nested-vector second-nested-vector))
Basically, count-matching-elements
just loops over the elements in each of the vectors and returns a series of 1 and 0's, depending on whether the corresponding elements match or not, and then the reduce
just adds them all up.
But now that I think about this some more, I might be tempted to use a loop/recur.
(defn count-matching-elements-alt
[v1 v2]
(loop [total-matches 0
[next-1 & more-1] v1
[next-2 & more-2] v2]
(if-not (and next-1 next-2)
total-matches
(if (= next-1 next-2)
(recur (inc total-matches) more-1 more-2)
(recur total-matches more-1 more-2)))))
(reduce + (map count-matching-elements-alt first-nested-vector second-nested-vector))
It’s longer, but I think it’s also much easier to come back later, read the code, and understand what’s going on, and that’s pretty important if you ever need to fix bugs.
What would be the most efficient way to match up two large-ish vectors of maps like this
[{:key1 someval1, :key2 someval2}, ...] and [{:key1 someval1, :key3 someval4}, ...] such that the matchup would be on key1; [{:key2 someval2, :key3 someval4}, ...]
that is, using key1 to match up key2/3 as the result?I'd start with (group-by :key1 coll)
you'd need some further edits to make it match your example (it leaves :key1 in each map), but does most of what you want but it does a reasonable first step for what you want
and you probably don't even need to remove :key1
on closer reading, if you used group-by you'd also want merge-with
Hm, interesting. Could be set/join solves most of the task. Looking at group-by now, thanks guys 🙂
nice, I'll need to remember set/join
user=> (clojure.set/join #{{:x 1 :y 2} {:x 2 :y 3}} #{{:x 1 :z 5} {:x 2 :z 6}})
#{{:x 2, :y 3, :z 6} {:x 1, :y 2, :z 5}}
Say you have a string like this "aaabcdddefffff"
, is there a way to split apart into continuous groups, i.e., ["aaa" "b" "c" "ddd" "e" "fffff"]
?
which is to say, I dunno how to do it with a regex, but that is a regular language which is exactly the sort of thing you should be able to do with a regex
@dharrigan (partition-by identity "aaabcdddefffff")
user=> (doc partition-by)
-------------------------
clojure.core/partition-by
([f] [f coll])
Applies f to each value in coll, splitting it each time f returns a
new value. Returns a lazy seq of partitions. Returns a stateful
transducer when no collection is provided.
(->> (partition-by identity "aaabcdddefffff")
(mapv (partial mapv str)))
(->> (partition-by identity "aaabcdddefffff")
(mapv (partial apply str)))
or, if seeing partial apply str
makes you shudder
(->> (partition-by identity "aaabcdddefffff")
(mapv clojure.string/join))
I think a lot of people forget/don't know that str/join
has a 1-arity that joins with an empty string... I've used apply str
several times and then gone "Oh, yeah, str/join
!" 🙂
@dharrigan one last one using transducers:
(transduce
(comp
(partition-by identity)
(map clojure.string/join))
conj
"aaabcdddefffff")
that makes me almost think for a moment that (into "" ... ...)
would be useful
@noisesmith I guess that works as well:
(into
[]
(comp
(partition-by identity)
(map clojure.string/join))
"aaabcdddefffff")
Oh - I meant the string combine after transduce part, but yeah that too
(defn into-str
"reduce coll into a String, given a transducer"
[xf coll]
(transduce (comp xf (map str))
(fn
([] (StringBuilder.))
([^StringBuilder sb] (.toString sb))
([^StringBuilder sb s] (.append sb ^String s)))
coll))
I copypaste that into a lot of projects @noisesmith
yeah, cool