Fork me on GitHub
#beginners
<
2022-04-19
>
littleli07:04:04

TIL: (deref ref) has a (deref ref timeout-ms timeout-val) arity. Interesting!

clojure-spin 1
agile_geek08:04:58

AFAIK that variant is only applicable to Promises and Futures (that block if not delivered/realised) but I use it quite a lot on Promises in testing async functions to prevent tests blocking forever.

littleli08:04:46

You're right. It's only useful when there is a blocking operation involved. Atoms and Agents are not blocking access to a state. I just didn't know about this arity to exist until I was reading clojure source code. That's why reading (other people) code matters 🙂

abishek10:04:26

Does anybody here work with ROS stuff using clojure? I’d love to learn a few things…

leifericf10:04:16

What does “ROS” mean? ☺️

abishek11:04:03

Yes 😅

👍 1
leifericf12:04:41

Cool! I don't know anything about that. I was just curious what it meant 😄

Roee Mazor12:04:56

Hi, I want to add a middleware to reitit that will count the number of requests currently in the server (increment when a request comes in and dec when it ends), how do I do that? planning to use metrics-clojure

Roee Mazor12:04:36

I tried adding some middleware like this:

{:name    ::format-request
                               :spec    ::spec
                               :compile (fn [ctx ctx2]
                                          (log/log-map :error nil nil "str" {:ctx ctx :ctx2 ctx2})
                                          {:wrap (fn [x] (println (str "str" {:ctx3 x})) x)})}
but it does nothing sadly, at least the server works but no prints when requests are sent

Ben Sless13:04:25

Why not instrument the server's thread pool?

Roee Mazor13:04:56

sure, how do I do that? using jetty

Ben Sless13:04:16

If you look at the executor service constructor, you can pass it a queue. Wrap it and inc counters for add and remove

Ben Sless13:04:45

I'd do it with reify into the queue interface

Ben Sless13:04:15

Iirc jetty lets you pass your own executor service, right?

Roee Mazor13:04:32

I don’t know, but I will check, thx!

Roee Mazor13:04:39

is putting a gauge on the default threadpool ok in your opinion?

(.getIdleThreads (.getThreadPool server))
(.getThreads (.getThreadPool server))

Ben Sless14:04:08

Why not have two counters, for takes and puts? That encompasses all the information you need, no?

Roee Mazor15:04:27

decided to go with both. got a gauge and a counter (dec and inc). the gauge feels more “right” cause I don’t need to wrap stuff. the counter feels more “right” since it is always “right” and you don’t have to call some function to fetch it. will see what I decide 😄

Ben Sless15:04:34

Counters are simpler. Also, when you have two, you can compare your ingress rate and your processing rate

Roee Mazor15:04:52

I have timers there too, should do the trick, right?

Ben Sless15:04:34

Too much 😄 If you have a counter, the derivative gives you rate I don't see why you need anything besides two counters

Ben Sless15:04:59

Although you can time the processing duration at the pool

Ben Sless15:04:20

Though you can't guarantee your task finishes on the pool or on another pool

Roee Mazor07:04:24

went with both for now, will update how it goes 🙂

leifericf12:04:00

In my Python projects, I use https://github.com/theskumar/python-dotenv to load environment variables from a .env file in my project root directory, which I add to my .gitignore. The library ensures that I don’t commit any secrets (API keys, etc.) to my Git repo and that environment variables are correctly loaded. What would be an idiomatic way of doing the same thing in a Clojure project? How do you manage your secrets and load them as environment variables?

Roee Mazor12:04:07

I would load a json / edn

👍 2
leifericf12:04:24

@U031X1BLY5U, How would you load the variables from EDN into the environment/REPL? I thought there might be some de facto standard Clojure library that takes care of that.

delaguardo13:04:08

you can keep doing the same and read secrets from environment variables. (System/getenv "VAR_NAME")

👍 1
Roee Mazor13:04:31

jsonista could read it for you

delaguardo13:04:32

that also mean you have to prepare your environment in production. eg. load .env file before running uberjar

Ben Sless13:04:24

My favorite way of dealing with configuration until now has been juxt/aero, which gives you edn with some useful reader tags, such as environment variables, string interpolation and profiles. You can also include local files

👍 1
👀 1
Ben Sless13:04:55

That way you can have a local file with private settings and a production file as part of a repository

didibus15:04:39

There are some libraries that do something similar. But as others have said, I think using configuration is better as well. Simply put, don't use environment variables, instead have a config file with secrets in them, could be EDN, and load that. Then when you deploy, you push a similar config file onto your machine where the file only has permissions to be read by the user that is running your application.

👍 1
didibus15:04:00

Or, if you prefer using environment variables, even though I think it's less secure, you can have a .env file in edn format and write yourself something simple like:

(defn get-env
  [k]
  (or (System/getenv k) (get (edn/read-string (slurp ".env")) k))
That will get you the environment variable if one exists, or otherwise the key from the edn map from your .env file. Generally this is all a library like dotenv does, except it uses a weird property-file like format for the .env file. If you want that as well I found https://github.com/LynxEyes/dotenv.clj That does exactly what you want.

😮 1
leifericf16:04:19

Thanks for all your advice! I'll probably go for the EDN config file approach instead of environment variables. But I'll check out those other ideas and links as well.

Jose Varela22:04:32

@U01PE7630AC I bumped into this while working through some compojure tutorials: https://github.com/weavejester/environ

👀 1
didibus22:04:54

I think someone already mentioned juxt/aero, but I think they have it pretty well figured out for managing config and secrets

👀 1
jumar03:04:28

For secrets, you can use services like AWS Secrets Manager

agigao08:05:25

https://github.com/yogthos/config has been dealing with my env variables for quite some time.

👀 1
V13:04:42

Is there a better way to write this? I want to check if the values of a vector is also contained in a specified set. If they are not, i want to throw an error.

(def allowed-tags #{:test1 :test2 :test3})

(defn ensure-correct-tagging [tags]
  (when (some false? (map #(contains? allowed-tags %) tags))
    (throw (Exception. "Specified tags are not allowed"))))
This works, but i was wondering if using some is even necessary. Is there a smarter way?

pyry13:04:04

Looks sensible to me, though there are a few tricks you might (or not) wish to use.

pyry13:04:04

First, it's idiomatic to use a set as a predicate: instead of (map #(contains? allowed-tags %) tags), you can instead write (map allowed-tags tags).

Bart Kleijngeld13:04:06

I'm still new, so if this not idiomatic or correct please correct me. But instead of the some and map construction, you could just do a subset check:

(subset? tags allowed-tags)
This seems to work with tags being a vector also Edit: subset? is available through the clojure.set library

pyry13:04:52

Using allowed-tags as a function will return the tag in question or nil, if not found in the set.

pyry13:04:28

I'd consider something like the following pretty idiomatic:

(when (seq (remove allowed-tags tags))
  (throw (Exception. "Specified tags are not allowed")))
This makes use of (seq ...) returning a nil if given an empty collection.

Bart Kleijngeld13:04:20

Any reason to prefer that over my subset solution? 🙂 I personally think that more clearly states intent

pyry13:04:22

clojure.set/subset? does convey the intention very well! I think the only drawback is that one really ought to use the function with sets, not collections of a generic kind.

pyry13:04:21

For instance, subset? internally calls clojure.core/contains? which will not perform a linear search:

(contains? [:b :c :d] :d)
=> false

2
Bart Kleijngeld13:04:09

Thanks, learned something valuable there!

🎉 1
vanelsas13:04:30

You could do it slightly more readable with doseq (I think):

(defn ensure-correct-tagging [tags]
  (doseq [tag tags]
    (when-not (some #(= tag %) allowed-tags)
      (throw (Exception. "Specified tags are not allowed")))))

V14:04:24

Thank you all very much for the reply 🙂 All suggested solutions look much better

🙂 1
Bart Kleijngeld14:04:38

@UCYS6T599 Do I understand correctly that this would work properly?

(subset? (set tags) allowed-tags)

pyry14:04:41

Yup, looks good to me!

👍 1
vanelsas14:04:03

@U03BYUDJLJF it assumes that the arguments are sets. It won't work for anything else though, so that would cause all kinds of issues if one of the arguments is not a set. Also (set nil) would always match, that may be a side effect you might not want?

Bart Kleijngeld15:04:09

Yeah as pyry explained it's not guaranteed to work unless the inputs are sets indeed. That's why I changed tags to (set tags) in my later solution. Good point about the (set nil) , I didn't think about that

nottmey15:04:55

Is there a transducer versions of conj and cons ? (Adding something at the end or beginning of a collection/iterator/stream. Ofc. under the assumption that conj only happens when it’s finite.)

Ben Sless15:04:23

I happened to write an append transducer, not too complicated, though I might have an error https://github.com/bsless/fast.csv/blob/master/src/bsless/fast/csv.clj#L96

gratitude-thank-you 1
nottmey15:04:42

yea I guess I need to write something like this myself :thinking_face:

nottmey15:04:11

ok ([result] (rf (rf result sep))) seems very straight forward 😄

Ben Sless15:04:42

Yeah, but you should probably check reduced before

nottmey15:04:38

Oh yea I always wonder that myself, when to check for it 🙈

flowthing15:04:04

Guess you could also consider using a deque or a finger tree instead?

nottmey15:04:45

that sounds… more advanced than I need it ^^

flowthing15:04:54

Fair enough. 🙂

Alex Miller (Clojure team)15:04:50

doesn't really make sense, but cat is kind of similar

Alex Miller (Clojure team)15:04:58

although cat is concatenating elements of the source, not a specific value

nottmey15:04:22

Oh, hmm. I was trying to find a better way for chaining something like this:

(->> ["b" "c" "d"]
     ;; other steps
     (#(conj % "e"))
     ;; further steps
     (#(concat ["a"] % ["f"])))
(the other steps are easily transduceable, only the concat and and conj parts don’t seem to have idomatic solutions in my current knowledge.)

Alex Miller (Clojure team)15:04:55

I mean, I guess could make prepend and append transducer functions

nottmey15:04:38

yea, will try it out and share the results where I ended up 😄

nottmey16:04:59

I guess this should do the trick

(defn append
  "Transducer which continues with a collection of values if completion is reached."
  [^Collection coll]
  (fn [step]
    (let [reduced-preserving-step #(let [ret (step %1 %2)]
                                     (if (reduced? ret)
                                       (reduced ret)
                                       ret))]
      (fn
        ;; init
        ([] (step))
        ;; completion
        ([result] (step (reduce reduced-preserving-step result coll)))
        ;; perform step
        ([result input] (step result input))))))
(defn prepend
  "Transducer which prepends a collection of values as the leading results until exhausted."
  [^Collection coll]
  (fn [step]
    (let [reduced-preserving-step #(let [ret (step %1 %2)]
                                     (if (reduced? ret)
                                       (reduced ret)
                                       ret))
          consumed?               (volatile! false)]
      (fn
        ;; init
        ([] (step))
        ;; completion
        ([result] (step result))
        ;; perform step
        ([result input]
         (if @consumed?
           (step result input)
           (do
             (vreset! consumed? true)
             (step (reduce reduced-preserving-step result coll) input))))))))

Alex Miller (Clojure team)15:04:59

In this example you are switching between coll operations and seq operations which is why it looks weird to begin with

Alex Miller (Clojure team)15:04:54

Generally, I try not to force that into a single chain

nottmey15:04:59

Ok 😅 Thank you Alex ☺️

j4m3s15:04:55

Hi ! I have encountered a somewhat surprising behavior and I'd like to make sure I understood it correctly.

(apply and '(true false))
Doesn't work
(apply 'and '(true false))
works. I guess the hint here is that both apply and and are macros so you need to "delay" the application of the macro and until after apply, thus needing this '. Should I understand that the macro and is "expanded" at the same time apply is, thus leading to my problem ? Thanks by advance 🙂.

dpsutton16:04:56

(apply 'and '(true false) doesn’t work how you think. 'and is a symbol which when invoked as a function will attempt to look itself up in an associate collection. ('and true false) is “look up ’and in true, and if not found, return the not-found value false”

dpsutton16:04:25

Macros don’t work on values they work on syntax and forms. Consider this bad example here:

(def x ['foo 1])
(let x (+ foo 3))
the let macro needs a literal vector of binding symbol and value, not something whose value is a vector

j4m3s16:04:16

Mmmmmm I see your point. So what would be the correct way of doing this ? (reduce and arr) ? (arr being a list)

j4m3s16:04:33

Didn't know about it but indeed !

j4m3s16:04:52

Thanks for the help and clarification, much appreciated 🙏