Fork me on GitHub
#beginners
<
2020-06-03
>
Daniel Östling06:06:38

In trying to get better at well written Clojure, does anyone have some projects to point to for study?

hindol06:06:55

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.

dharrigan07:06:05

Sean Corfield's repos, like next.jdbc are great too!

Daniel Östling09:06:53

Thanks guys, I appreciate it 🙂 And sorry for late reply, meetings ...

tio08:06:22

What is wrong with this line?

(api/mutes-users-create
 creds :params {:screen_name "jack"})

tio08:06:53

This is the error I get:

Syntax error (NullPointerException) compiling at (src/..../core.clj:7:1).
null

didibus09:06:39

Most likely creds is nil

tio18:06:42

Creds is not nil

didibus00:06:33

Well then its probably something inside that function that would be nil

didibus01:06:04

It helps if you look at the full stacktrace, it could show you the exact line where the exception is first thrown

didibus01:06:37

Also because it says compiling at... I think it might be from inside a macro, not sure

tio05:06:36

Yep; creds was nil face palm

tf11:06:10

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.

❤️ 1
Daniel Östling12:06:15

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/

👍 3
Joshua12:06:58

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/

👍 1
Daniel Östling12:06:29

Same here about tooling. I ended up picking Emacs/Cider and Boot, for example.

👍 1
Joshua12:06:04

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)

tf12:06:07

> 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 😄

tf12:06:12

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…

Joshua12:06:15

You know when books say "We really want you to do try our examples?" and you never do? First time I've ever listened 😆

😂 1
Joshua12:06:01

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

Joshua12:06:48

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

😬 1
Joshua12:06:02

So I'd say, do whatever is closest what you know and minimises the learning time to get you going

👌 1
Daniel Östling12:06:10

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 🙂

👌 1
Daniel Östling12:06:30

Mastering Emacs is a great book, if that's the route you want.

👌 1
noisesmith13:06:22

> 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

👌 1
tf13:06:52

Thanks again for all the resources, guys! The Web development with Clojure book and Brave clojure both look really nice!

kmyokoyama14:06:36

An alternative is Intellij IDEA + Cursive plugin. It works very well.

👌 2
kmyokoyama14:06:23

You can also look for libraries to use here: https://www.clojure-toolbox.com/

👌 2
fabrao13:06:45

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}] ?

Darin Douglass13:06:55

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)

noisesmith13:06:44

or even (update my-map "2020-05-20" inc)

kmyokoyama13:06:33

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?

kmyokoyama13:06:09

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).

noisesmith14:06:35

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

noisesmith14:06:09

the right way to solve a clojure problem is usually to turn the data into the shape where the problem is easily solved

Darin Douglass14:06:04

exactly. if this is the end format, i'd recommend a different format for processing then a transformation at the very end

kmyokoyama14:06:15

Yes, nice tips. Thank you

noisesmith14:06:50

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})

noisesmith14:06:09

(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))

noisesmith14:06:20

also, the sanity test:

user=> (= account-vec (denormalize (normalize account-vec)))
true

kmyokoyama14:06:48

Is it ok to do all this transformation to update one field from performance perspective?

Darin Douglass14:06:32

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

kmyokoyama14:06:03

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?

kmyokoyama14:06:11

Sorry for playing the devil's advocate here, I really would like to fully understand it

fabrao14:06:14

Thank you for your help, I´ll try using each

noisesmith14:06:47

@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

noisesmith14:06:12

(where there are variables like the size of the input of course...)

noisesmith14:06:02

the ideal case of course is changing the canonical record so you don't ever have to convert

kmyokoyama14:06:25

I see. Yes, I agree that changing the original data structure would be the ideal solution. Thank you, @noisesmith and @UGRJKK74Y for the insights.

dev.4openID14:06:38

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?

kmyokoyama14:06:13

@UEGT2541J, it is very hard to understand code without proper formatting. Could you put it in a gist, please?

dev.4openID14:06:23

I deleted the message and will put up again with gist

dev.4openID14:06:21

Resubmitted to slack

mafcocinco17:06:50

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.

noisesmith17:06:37

right, resources are not files, and file apis don't work on them except accidentally

noisesmith17:06:19

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

alexmiller17:06:57

I think it actually is hard to write in a totally generic way

noisesmith17:06:11

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

noisesmith17:06:26

even narrowing it to directories and jar files would suffice

alexmiller17:06:34

I think it’s worth considering why this is hard and maybe why you shouldn’t do it

alexmiller17:06:15

Certainly the op’s use case seems like a bad idea and it being hard is a signal worth listening to

mafcocinco18:06:28

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.

mafcocinco17:06:33

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

amirali18:06:30

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 advance

manutter5118:06:44

You can use reduce for things like that.

(reduce (fn [counted i]
          (if some-condition
            (inc counted)
            counted))
        0
        (range 5))

🙏 1
amirali20:06:25

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 fn

manutter5121:06:40

You 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]))

🙏 1
manutter5121:06:41

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.

👍 1
amirali21:06:18

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?

manutter5121:06:19

Can you give me an example of some nested vectors you want to check?

amirali21:06:16

[[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]

manutter5121:06:04

Ok, let me think a minute…

👍 1
manutter5121:06:49

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))

manutter5121:06:19

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.

manutter5121:06:22

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))

amirali21:06:59

genius! Thank u so much!

👍 1
manutter5121:06:01

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.

jkrasnay18:06:29

Or (count (filter condition (range 5)))

🎯 3
alexmiller19:06:44

^^ try to think in collections, not in elements

🎯 2
Daniel Östling20:06:38

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?

noisesmith20:06:48

I'd start with (group-by :key1 coll)

noisesmith20:06:17

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

noisesmith20:06:28

and you probably don't even need to remove :key1

hiredman20:06:23

there is also clojure.set/join

👍 2
noisesmith20:06:54

on closer reading, if you used group-by you'd also want merge-with

Daniel Östling20:06:29

Hm, interesting. Could be set/join solves most of the task. Looking at group-by now, thanks guys 🙂

noisesmith20:06:25

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}}

👍 1
dharrigan20:06:34

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"]?

hiredman20:06:39

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

ghadi20:06:25

@dharrigan (partition-by identity "aaabcdddefffff")

ghadi20:06:11

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.

dharrigan20:06:29

that's great, gets closer, but the output is like this:

phronmophobic20:06:35

(->> (partition-by identity "aaabcdddefffff")
     (mapv (partial mapv str)))

phronmophobic20:06:10

oh whoops

👍 1
phronmophobic20:06:25

(->> (partition-by identity "aaabcdddefffff")
     (mapv (partial apply str)))

dharrigan20:06:07

wunderbar! thank you @ghadi @sfyire and @hiredman 🙂

dharrigan20:06:34

so elegant 🙂

seancorfield21:06:54

or, if seeing partial apply str makes you shudder

(->> (partition-by identity "aaabcdddefffff")
     (mapv clojure.string/join))

👍 2
seancorfield21:06:03

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!" 🙂

😁 1
👍 2
raspasov21:06:48

@dharrigan one last one using transducers:

(transduce
  (comp
    (partition-by identity)
    (map clojure.string/join))
  conj
  "aaabcdddefffff")

noisesmith21:06:02

that makes me almost think for a moment that (into "" ... ...) would be useful

raspasov22:06:01

@noisesmith I guess that works as well:

(into
  []
  (comp
    (partition-by identity)
    (map clojure.string/join))
  "aaabcdddefffff")

noisesmith22:06:08

Oh - I meant the string combine after transduce part, but yeah that too

ghadi22:06:14

(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))

💯 2
ghadi22:06:31

I copypaste that into a lot of projects @noisesmith

ghadi22:06:46

not #beginners material, but essentially what you're asking about