Fork me on GitHub
#beginners
<
2022-03-11
>
Bryan B02:03:17

Hi, I’m interested in learning clojure. The book Clojure for the Brave and True keeps coming up as a good source. If not mistaken this book was released about 7 years ago. I don’t know how much clojure has changed since then. Is the book still relevant or is it outdated?

Cora (she/her)04:03:16

you didn't ask but if you already know how to program then Getting Clojure is an amazing book for learning Clojure

👍 1
Bryan B10:03:51

Thanks. I’ll check it out.

practicalli-johnny13:03:09

There are lots of great resources for learning Clojure https://clojure.org/community/resources

Cora (she/her)13:03:17

and also @U05254DQM puts out this very excellent resource https://practical.li

❤️ 2
Alex Miller (Clojure team)02:03:07

Language stuff is still relevant, tooling stuff maybe less so

👍 1
Alex Miller (Clojure team)02:03:13

it is very rare for existing Clojure language features to change, but some things have been added since then

👍 1
Bryan B02:03:55

Thank you. That’s great to know.

quan xing03:03:07

(defmethod ig/init-key :adapter/jetty [_ opts]
  (let [handler (atom (delay (:handler opts)))
        options (-> opts (dissoc :handler) (assoc :join? false))]
    {:handler handler
     :server  (jetty/run-jetty (fn [req] (@@handler req)) options)}))
what's mean "@@handler" , is it the first deref atom and after deref delay?

R.A. Porter03:03:30

Dereferences the atom, which contains a delay that also needs to be deferenced.

ghadi03:03:27

i'm not sure why there is a delay inside an atom though

ghadi03:03:50

it's actually covered in the integrant readme

popeye08:03:16

hi team I am exploring memoize function and when I run below code in IDE

(defn myfunc[a] (println "doing some work") (+ a 10))

(def myfunc-memo (memoize myfunc))

(def a 5)
(println (myfunc-memo a))

popeye08:03:11

I am getting below response every time

doing some work
15

popeye08:03:29

I expect it should return 15 only! why it returning doing some work ? I thought since I am running in IDE all the functions reload again! ( so it runs well in repl) So how can we make use memoize in production code

jumar08:03:05

@popeyepwr if you run the whole code block every time then you keep redefining all the vars (functions) so my-func-memo is a fresh thing everytime you run it. Try to only load it once and then just evaluate (my-func-memo a) a few times and see if it makes a difference.

popeye08:03:37

@U06BE1L6T Thanks for your response! How can we make a single function separated ?

jumar08:03:16

I'm not sure what you're asking for O:-)

popeye09:03:19

I have a function where I am calling from webservice,, So separating memoize function would be difficult in my case

jumar11:03:31

@popeyepwr I think there's some confusion here. I was talking about the case when you are running the REPL and working on the application. To see how it works, you can do as I suggested, that is evaluate the whole code block only once and then try to call (my-func-memo a) , perhaps in the REPL, a few times. I'm not sure what you mean by "separating memoize function would be difficult in my case" but you definitely don't need to move it to another namespace. You can simply keep it as it is now

jumar11:03:09

I think your trouble might be that you have only learned how to evaluate the whole buffer/file/namespace and the individual forms (like defn)

popeye12:03:42

@U06BE1L6T sorry for the late response , Yeh I understood how it behaves in repl, But how about production code where it calls the function again and again on refresh

jumar14:03:28

What do you mean by "refresh"? There's typically no such thing in the production code.

popeye14:03:00

page refresh (page load)

jumar14:03:18

Ah, I see. Sure, that's just calling the function. So the first time it will do the actual calculation, after that it will use the cached (memoized) value.

Jon Boone09:03:12

@popeyepwr — what happens when you evaluate your buffer in your IDE, the evaluate the last expression only in the REPL?

jumar11:03:40

it evaluates all the forms one-by-one.

Jon Boone13:03:16

@popeyepwr — did you solve your issue?

popeye14:03:57

Have to troubleshoot some more, since I am playing with production code

popeye12:03:55

I have a function inside a function, How to stop calling second function during recursion ?

Jon Boone13:03:48

Can you elaborate? Do you want to stop under certain conditions?

popeye13:03:30

nope, I do not want second function to called,

delaguardo13:03:48

Direct answer: You can create an exception object and analyze its stacktrace to see if there are multiple calls to some function. But i would not advise you to do that. Change the function instead

Jon Boone13:03:34

To clarify: is the second function defined inside the lexical scope of the first and subsequently called by the first? Or is it just called?

popeye13:03:22

I read in some doc, that we can make use of promise ,

popeye13:03:28

but I do not remember which is that function, like once promise object is already derefered, thenit would not call that function again, I lost that doc

oddsor14:03:14

I’m not sure if I understand the intent, but I can think of three ways to avoid evaluating the second function without things becoming too “magical”: • The arguments passed into the first function somehow affects if the second should run, so for example if you pass in a counter during recursion then it only runs when the counter equals 1 (or just not at all). • If the goal is to run the function only once, then store the evaluated value in an atom and during recursion check if the atom has changed from whatever the default value is • Use a dynamic var to turn evaluation on or off depending on the context

(def ^:dynamic *skip-evaluation* false)
(defn second-worker []
  (prn "I do things!"))

(defn first-worker [x]
  (when-not *skip-evaluation*
    (reset! worker-result (second-worker)))
  (prn "Loop number " x)
  (inc x))

(first-worker 0)

(binding [*skip-evaluation* true]
  (loop [x 0]
    (if (> x 5)
      x
      (recur (first-worker x)))))
In this example the second-worker doesn’t run during the loop-recur

Jon Boone14:03:23

@popeyepwr — can you share function definition where promise is created?

popeye14:03:42

Sorry! I saw that doc some days back! will try to find it out

oddsor21:03:11

Thread necromancy! I just stumbled upon the delay-function which might help you achieve what you’re expecting: https://clojuredocs.org/clojure.core/delay

oddsor21:03:20

(you have to define the delay outside the function body and “deref” it to make sure it runs once)

Fredy Gadotti17:03:28

Hey guys, what is the best way to refactor the following code?

(defn printer-error [string]
  (str (count (filter #(> (int %) 109) (seq string))) "/" (count string)))

(printer-error "aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbmmmmmmmmmmmmmmmmmmmxyz")

; Output: 3/56
I tried to convert to thread first, but the filter expect receive as the last argument and count expect the result from filter.
(defn printer-error [s]
  (-> s
      seq
      (filter #(> (int %) 109))
      count
      (str "/" (count s))))
The second approach was using as threading, but the problem now becomes on the result of filter.
(defn printer-error [s]
  (as-> s string
        (seq string)
        (filter #(> (int %) 109) string)
        count
        (str "/" (count string))))
This approach works but I believe could be better somehow.
(defn printer-error [s]
  (let [total-invalid-chars (count (filter #(> (int %) 109) s))]
    (str total-invalid-chars "/" (count s))))

Martin Půda17:03:12

(defn printer-error [s]
  (-> (filter #(> (int %) 109) s)
      count
      (str "/" (count s)))) 

🙌 1
Ferdinand Beyer17:03:51

Your intuition was spot on. This is the best, by far:

(defn printer-error [s]
  (let [total-invalid-chars (count (filter #(> (int %) 109) s))]
    (str total-invalid-chars "/" (count s))))
When reading your code without context, I needed to understand your intention. The function is supposed to find invalid characters, then print how many are invalid in relation to the length of the string. This version represents exactly that, no magic.

ghadi17:03:50

(Personally: I'd let-bind invalid-chars rather than their count, then (str (count invalid-chars) "/" (count s)) )

ghadi17:03:43

docstrings are always useful 🍰

Ferdinand Beyer17:03:52

My point was: Use let instead of trying to thread it all together

Fredy Gadotti18:03:26

@U01RL1YV4P7 your code work perfectly! I though I need to convert to a seq before send into filter function

Fredy Gadotti18:03:54

@U050ECB92 Agree with the count outside would be better in that solution 😄

Jacob Rosenzweig17:03:17

Dumb question but what would be the Clojure way for transforming a list of words into a list of sentences? E.g. [ “Hello.” “How” “are” “you?” “I” “am” “fine.” ] => [ “Hello.” “How are you?” “I am fine.“] I’m thinking it’ll involve a reduce.

Martin Půda18:03:06

(->> (-> (clojure.string/join " " [ "Hello." "How" "are" "you?" "I" "am" "fine."])
         (clojure.string/split #"(?<=\.)|(?<=\?)|(?<=\!)"))
     (mapv clojure.string/trim))
=> ["Hello." "How are you?" "I am fine."]

metal 1
Ferdinand Beyer18:03:31

(defn group-words [words]
  (let [n (atom 0)]
    (partition-by (fn [w]
                   (let [i @n]
                    (when (re-matches #".*[.?!]$" w)
                      (swap! n inc))
                    i))
                  words)))
(group-words ["Hello." "How" "are" "you?" "I" "am" "fine."])
; => (("Hello.") ("How" "are" "you?") ("I" "am" "fine."))
(->> words group-words (mapv (partial clojure.string/join " ")))
; => ["Hello." "How are you?" "I am fine."]

metal 1
Jacob Rosenzweig18:03:03

partition-by and mapv both look like good solutions. Thanks.

tomd23:03:23

The first suggestion can be simplified by including space(s) after the lookbehind:

(-> (clojure.string/join " " test-text)
    (clojure.string/split  #"(?<=[.?!]) +"))
;; => ["Hello." "How are you?" "I am fine."]

Ferdinand Beyer12:03:51

Another pretty elegant solution involves spec:

(require '[clojure.spec.alpha :as s])
(defn stop? [w] (re-matches #".*[.?!]" w))
(s/def ::sentence (s/cat :words (s/* (complement stop?)) :stop stop?))
Then conform the input:
(s/conform (s/+ ::sentence) ["Hello." "How" "are" "you?" "I" "am" "fine."])
; => [{:stop "Hello."} {:words ["How" "are"], :stop "you?"} {:words ["I" "am"], :stop "fine."}]
Now it’s just a matter of running map over the result to conj :words and :stop and join:
(->> ["Hello." "How" "are" "you?" "I" "am" "fine."]
     (s/conform (s/+ ::sentence))
     (map (fn [{:keys [words stop] :or {words []}}]
            (conj words stop)))
     (map (partial str/join " ")))
; => ("Hello." "How are you?" "I am fine.")
Note that you can easily extend this and get input validation for free.

randomm char19:03:11

\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

Andreas Guther20:03:20

Hi! I am fighting configuration problems for getting monger connected to Amazon DocumentDB via TLS using a connection URI. The same URI works perfectly fine with some node.js programs. I am using the following template and pass in the parameters via format

...:require [monger.core :as mg]...
...
(def connection-uri-template "")
(def connection-uri (format connection-uri-template user-name password db-server database ssl_ca_certs tlsCAFile))

(let [uri connection-uri {:keys [conn]} (mg/connect-via-uri uri)]

  (do
    (println connection-uri)
    (def result (enforcement-proxy-groups-simple conn))
    (println result)
    )
  (mg/disconnect conn))
I am getting a java.net.SocketTimeoutException: Read timed out. I am missing something here. I wonder if someone could point me to a working configuration. Do I need a keystore file because we are running on the JVM? BTW, I am using Java 11.0.11, but I do not think that matters.

Andreas Guther21:03:37

The path to the tlsCAFile is encoded as I have it for the Node.js application.

hiredman20:03:21

You may need to url encode some things