beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
athomasoriginal 2021-03-17T00:14:32.048100Z

That’s a good point. Let me look into the tooling more. With atom/chlorine I normally use execute from whichever namespace i’m currently in. Thus, if i’m in ns.some.place when I execute commands they are from that namespace.

2021-03-17T01:20:30.048300Z

I have a function that combines (apply require clojure.main/repl-requires) with loading my utilities from user.clj into the current ns, that gives me the standard things like doc, pprint, source in any ns

👍 1
Chase 2021-03-17T13:47:28.049900Z

Why does (clojure.string/split (clojure.string/lower-case "Hello world") #"\W+") give me my intended behavior but (->> "Hello world" (str/lower-case) #(str/split % #"\W+") doesn't? I think I am misunderstanding how to use anonymous functions inside a threading macro.

alexmiller 2021-03-17T13:49:35.050300Z

yeah, you can't do that - it's helpful to look at the expansion:

user=> (pprint (macroexpand '(->> "Hello world" (str/lower-case) #(str/split % #"\W+"))))
(fn* [p1__8#] (str/split p1__8# #"\W+") (str/lower-case "Hello world"))

alexmiller 2021-03-17T13:51:25.051300Z

the #() is expanded to (fn* [...] ...) first, and then the ->> is threaded into the last expression of the (fn* ...), which is not what you intended

Chase 2021-03-17T13:52:17.052200Z

ahhh, ok. Very good. I'll take a different approach. This is part of a bigger transformation so I was trying to shoehorn it into a threading macro

alexmiller 2021-03-17T13:52:36.052600Z

you can make it work by wrapping the #():

(->> "Hello world" (str/lower-case) (#(str/split % #"\W+")))

Chase 2021-03-17T13:52:58.053100Z

ahh. is that idiomatic though?

alexmiller 2021-03-17T13:53:00.053300Z

but generally I find that to be more confusing to read than not doing it in the first place

Chase 2021-03-17T13:53:13.053500Z

fair enough

2021-03-17T13:57:45.053800Z

@chase-lambert See also: as->

Chase 2021-03-17T13:58:51.054400Z

Yep, I was looking at those. Of course, this was the only part of like 5 step process that wasn't thread last

Chase 2021-03-17T13:59:35.054700Z

How would you approach this:

(let [s "The foo the foo the\ndefenestration the"] (doseq [w (->> s (str/lower-case) (#(str/split % #"\W+")) (frequencies) (sort-by val >) (map #(str (key %) " " (val %))))] (println w)))

Chase 2021-03-17T14:00:05.055100Z

not sure why the formatting got a little funky there

Chase 2021-03-17T14:00:26.055600Z

I was just playing around with this while reading this article: https://benhoyt.com/writings/count-words/

2021-03-17T14:00:38.056Z

(Triple backticks to format multi-line code)

2021-03-17T14:01:49.056300Z

What about something like this:

(let [s "The foo the foo the\ndefenestration the"
      word-split #(str/split % #"\W+")]
  (doseq [w (->> s
                 (str/lower-case)
                 (word-split)
                 (frequencies)
                 (sort-by val >)
                 (map #(str (key %) " " (val %))))]
    (println w)))

Chase 2021-03-17T14:08:29.056900Z

Yeah, I like this approach, ty

👍 1
Hagenek 2021-03-17T14:13:49.058800Z

Im doing some data validation stuff in clojure and I have encountered something I dont understand well enough yet. How do you gracefully handle e.g. AssertionErrors in Clojure? In another language I would return out of the function and console.log the error, while in Clojure I would need some If logic to not run the rest of the function? The validate-user-data returns a nil on successful validation, and the validation error as a map on unsuccesful Here is what I am working with:

(defn user-vector-builder
  "Takes a user-map and returns a valdiated and formatted user vector
   expects keys :email and :age and both values should be of string type"
  [user-m]
  (try
    (assert (= (validate-user-data user-m) nil) (str "Invalid data: " (validate-user-data user-m)))
    (catch AssertionError e (println (:case  e))))
  [(:age user-m) ; Header: alder
   (:email user-m) ; Csv-header epost
   (if (>= (Integer/parseInt (:age user-m)) 18)
     "1"
     "0") ; Csv-header myndig
   (str (t/ago (t/millis 1337))) ; Csv-header timestamp
   ])

Hagenek 2021-03-17T14:14:27.059400Z

Do not hold back on critism of all parts of this code btw, I always want to improve as much as possible

gon 2021-03-17T14:46:26.061400Z

I would use a different approach here ... instead of try/catch

(if (validate-user-data ....)
  (do my-code-here)
  (do fail here))  

Chase 2021-03-17T16:03:39.063700Z

So back to my previous discussion about that count words benchmark article I posted earlier, I made the program into a babashka script to see how it compares to the other languages listed. I came up with this:

#!/usr/bin/env bb                                                                 
                                                                                  
(require '[clojure.string :as str])                                               
(require '[<http://clojure.java.io|clojure.java.io> :as io])                                               
                                                                                  
(defn read-lines [file]                                                           
  (with-open [r (io/reader file)]                                                 
    (doall (line-seq r))))                                                        
                                                                                  
(let [file (first *command-line-args*)                                            
      word-split #(str/split % #"\W+")]                                           
  (doseq [w (-&gt;&gt; (read-lines file)                                                
                 (str/lower-case)                                                 
                 (word-split)                                                     
                 (frequencies)                                                    
                 (sort-by val &gt;)                                                  
                 (map #(str (key %) " " (val %))))]                               
    (println w)))
It seems to be quite performant and looks way "simpler" to my biased self! What would you folks do differently?

dpsutton 2021-03-17T16:05:06.064300Z

https://chrispenner.ca/posts/wc is an excellent post with a line of thinking exactly towards this problem and about 13 different ways they approached it

2021-03-17T16:05:37.064600Z

you could use transducers instead of threading macro to avoid construction of intermediate collections

Chase 2021-03-17T16:15:05.064800Z

Ooh, that would be a good exercise. I don't know why but I've let this whole transducer thing intimidate me

2021-03-17T16:32:23.065Z

Using existing transducers is way less intimidating than implementing a new one (which is usually unnecessary), or fully understanding all aspects / tradeoffs of their implementation.