beginners

Zak Venter 2025-10-07T07:31:06.849269Z

If I have a function which only contains a loop inside, is there some cleaner way to write this, without obfuscating the loop variable names somehow?

(defn my-func
  [arg1 arg2 & rest]
  (loop [a1 arg1 a2 arg2 r rest]
    ...))

p-himik 2025-10-07T07:35:43.646739Z

If you don't need the original values arg1, arg2, rest, you can just reuse the names:

(loop [arg1 arg1
       arg2 arg2
       rest rest]
  ...)
And at that point, you can remove the loop altogether and recur to the function call itself. BTW, just in case - I'd advise against using rest as a name. It shadows a built-in clojure.core.rest which, if you aren't careful, can lead to sometimes rather hard to debug issues.

👍🏼 1
👏🏼 1
👌🏼 1
Zak Venter 2025-10-07T07:40:30.516249Z

Thank you! I wasn't aware I could reuse variable names like that, or use recur without a loop. Also thanks for the heads-up on using rest, do you have any advice for a function named "read"? should I manually namespace the function like "prefix-read"?

p-himik 2025-10-07T07:48:10.370999Z

The answer is "it depends". I myself try to avoid shadowing vars with locals, but somehow I'm more or less fine with shadowing a var from clojure.core with my own var - granted that there's (:refer-clojure :exclude [that-var]) in the ns form to make the shadowing explicit and to avoid the corresponding warning. If read is just the perfect name for that function, I would use it and add read to the exclusions from clojure.core. If the name can be altered a bit without sacrificing anything, I'd change it so avoid the exclusion hassle and to guarantee that there will be no accidental usages of your.ns/read where clojure.core/read was meant to be used. But I should also add that I almost never use :use or :refer. I almost exclusively use (:require [some.ns :as alias]) and then do alias/some-var, so all the context is crystal-clear. It doesn't help when you need to use read in that same namespace, but it definitely helps if read is intended to be used by other namespaces.

👍 1
hrtmt brng 2025-10-07T08:35:19.868699Z

(def my-func #
 (loop [arg1 %1 arg2 %2 r %&]
   ...))

👎🏼 1
⛳ 1
😃 1
hrtmt brng 2025-10-08T04:23:01.206699Z

Are there any downsides of using recur without loop? I mean, we want to have real real tail recursion. I heard a few times, that we will not get it because of JVM limitations. But we have it already. ?

2025-10-08T04:38:25.822119Z

Fn bodies and method bodies (in things like reify, etc) have implicit loops

2025-10-08T04:39:18.824669Z

So in effect you cannot use recur without loop, as those are the only places you can use it

James Amberger 2025-10-07T14:32:54.633089Z

Thinking out loud here: might/should there be an approach to logging where you can “catch” a log emission so it can be associated with a particular execution stack. Eg.

(do
(log "Calling frob for" user)
(frob "I, frob, will be calling (log \"Frobnication took 4324ms\") but I don't know \`user\` and don't want to have to know just for the sake of the log message))
so you’d want these two log messages associated, conceptually the lower one “indented” under the higher one

2025-10-07T14:33:46.944079Z

how would the outer context know to drop the inner context?

2025-10-07T14:34:28.659439Z

Unless you're going to write something like (try (stateful-log) (stateful-log) (stateful-log) (finally (clear-log-state)))

2025-10-07T14:36:12.837469Z

Could be regarded as "a poor man's MDC", or maybe the other way around

James Amberger 2025-10-07T14:36:28.325699Z

MDC not familiar to me

2025-10-07T14:36:32.954029Z

yeah, i was going to recommend MDC depending on what you need

James Amberger 2025-10-07T14:38:30.299729Z

Picturing logging output like

[timestamp] Frobbing for John >> [timestamp] I, frob...

Samuel Ludwig 2025-10-07T14:39:19.320869Z

So, mulog has something sort of like what you describe, if you use a trace, it'll associate all following logs in that execution-chain with a parent ID

Samuel Ludwig 2025-10-07T14:39:39.207979Z

(but, of course, its all just maps, not kindly-formatted string-outputs)

2025-10-07T14:40:15.990899Z

"mapped diagnostic context" - a feature of Log4j (and maybe others) - a way to stuff key/value pairs into a thread-local that the logging framework includes in log messages, basically, IIRC

❗ 1
2025-10-07T14:41:20.681249Z

MDC is less beautiful than @james.amberger had in mind, but on the other hand the log statements are not coupled at all

James Amberger 2025-10-07T14:42:22.963059Z

ok interesting

2025-10-07T14:42:24.408769Z

(And sometimes a bird in the hand is worth two in the bush)

James Amberger 2025-10-07T14:42:30.764069Z

(afk)

phronmophobic 2025-10-07T17:01:08.877999Z

flowstorm also has similar features https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_printer

1
❤️ 1
emccue 2025-10-08T15:23:13.878219Z

(MDC/put "user_id" (str ...))
(try
  ...
  (finally
    (MDC/remove "user_id")

👍 1
emccue 2025-10-08T15:23:38.813809Z

and then in most slf4j compatible things there is a way to get this info into the log line

emccue 2025-10-08T15:24:22.765489Z

looking at the docs now I also see this

emccue 2025-10-08T15:24:48.492019Z

(with-open [_ (MDC/putCloseable "user_id" (str ...)]
  ...)

👀 1
emccue 2025-10-08T15:25:10.143219Z

and i'm sure clojure logging adapters expose all this in their own way + some might provide different mechanisms

2025-10-08T15:31:56.854629Z

one thing to note about MDC is it's only strings, so you have to do JSON generation if you want to pass objects

emccue 2025-10-08T15:36:17.912229Z

(ns main
  (:require [clojure.tools.logging :as log])
  (:import (org.slf4j MDC)))

(defn -main
  []
  (with-open [_ (MDC/putCloseable "user_id" "123")]
    (log/info "Hello"))
  (log/info "world"))
[main] user_id=123 INFO  main - Hello
[main]  INFO  main - world

✅ 1
Gustavo Maia Oliveira 2025-10-07T18:46:10.599859Z

Hey everyone, it's me again, haha. I want to create a conditional in my Clojure API. Can anyone help me? Please teach me how to use the schema checker. After using it, it works, but it doesn't return the message in POSTMAN. Can anyone help me? I am trying to make this:

(if (= (s/check model/schema-keyword-age body) nil)
  (spit file-path/file-path
                         (json/write-str updated-list)
  {:status  200
    :headers {"Content-Type" "application/json"}
    :body    {:message (str "sucess!")}})
                 {:status  400
                  :headers {"Content-Type" "application/json"}
                  :body    {:message (str  "fail")}}))
In the IDE logs they show me these messages, but in POSTMAN I get the 500 and the internal server error: exception

dpsutton 2025-10-07T18:47:03.497599Z

what is the exception’s message?

dpsutton 2025-10-07T18:48:45.554159Z

spit takes multiple arguments, but that return of “success” seems to be passed as the options to spit which is almost certainly not what you want

dpsutton 2025-10-07T18:49:22.669109Z

and can i propose a simple way forward? Replace the conditional with something trivial until it is working and then change it to be more complicated with what you actually want to check

dpsutton 2025-10-07T18:49:38.168389Z

(if (even? 4)
  {:status  200
   :headers {"Content-Type" "application/json"}
   :body    {:message (str "sucess!")}}
  {:status  400
   :headers {"Content-Type" "application/json"}
   :body    {:message (str  "fail")}})

dpsutton 2025-10-07T18:50:07.667999Z

get this working. then switch to odd?. Then use the repl to make sure you understand how s/check works, then replace the s/check call over the even? or odd? call

2025-10-07T19:27:58.455999Z

If you don't have some middleware setup to transform the response into json then returning a map like that in :body may error

dpsutton 2025-10-07T19:31:50.299959Z

ah. it should be fine, but i guess it should be "\"fail\"" ?

(cheshire.core/generate-string "fail")
"\"fail\""

dpsutton 2025-10-07T19:31:53.303879Z

good eye

2025-10-07T20:05:34.792029Z

It is a map though, the string is under the :message key

dpsutton 2025-10-07T20:11:25.018419Z

oh you are right. i just spaced out on that. it looks so close to the ring map i glossed over it

Gustavo Maia Oliveira 2025-10-08T17:11:11.322099Z

Hello, thanks for the answers!! The first option that @dpsutton passes works to return the message:

(if (= (s/check model/schema-keyword-age body) nil)
  {:status  200
   :headers {"Content-Type" "application/json"}
   :body    {:message (str "sucess!")}}
  {:status  400
   :headers {"Content-Type" "application/json"}
   :body    {:message (str  "fail")}})
but I have to put this:
(spit file-path/file-path
      (json/write-str updated-list))
so when i do this it doesn't work:
(if (= (s/check model/schema-keyword-age body) nil)
  (spit file-path/file-path
        (json/write-str updated-list) {:status  200
                                       :headers {"Content-Type" "application/json"}
                                       :body    {:message (str "sucess!")}})
  {:status  400
   :headers {"Content-Type" "application/json"}
   :body    {:message (str  "fail")}})
you can help me with this, I would be very grateful!!!

dpsutton 2025-10-08T17:14:37.948949Z

(do (spit …) {:status 200 …})

dpsutton 2025-10-08T17:14:57.315699Z

if takes a test and then two “branches”. to combine two expressions into one you can use do

Gustavo Maia Oliveira 2025-10-08T17:48:27.039709Z

Yessss!!!! I did it like this:

(if (= (s/check model/schema-keyword-age body) nil)
  (do (spit file-path/file-path (json/write-str updated-list)) {:status  200
                                                                :body    {:message (str "A idade do usuário " name " com o id " user-id ", foi alterado de " old-age " anos, para " new-age " anos com sucesso!")}})
  {:status  400
   :body    {:message (str "A idade do usuário " name " com o id " user-id " não foi alterada. Verifique se o campo "age" está preenchido corretamente.")}})))
Now it's works, this function executes the logic and returns the message to the POSTMAN Thanks man, you're awesome!!!

dpsutton 2025-10-08T17:51:23.097999Z

glad you are unblocked!

❤️ 1