clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
2021-04-01T00:49:18.174700Z

I've been working with some dynamic variables for a little project I'm using, and it seems like they aren't working for me. My setup is like so: I have a namespace which creates a dynamic variable, and defines a macro which includes binding it. In another namespace, I use said namespace (this works the same when I require it too), define a function, and call it from within the body of a usage of the macro, and while in that function the binding value isn't visible. However, whenever I use the cider debugger and debug either the function being called, or the usage of the macro, it works as intended.

2021-04-01T00:49:54.175400Z

Is there some gotcha with dynamic variables that would cause this? Or is there something else about my setup which is going wrong?

2021-04-01T00:58:17.175500Z

My guess would be you are binding the macro during macro expansion, but not in the expansion of the macro

2021-04-01T00:59:21.175900Z

behavior is identical when the usage of the macro is substituted for its macroexpansion

2021-04-01T01:00:50.176Z

And what is that?

2021-04-01T01:02:08.176400Z

(restart-case (analyze-logs '("LOG: Hello, world!"
                              "LOG: This is a second log entry"
                              "LOG: "
                              "LOG: hey"
                              "blah"
                              "ERROR: "))
  ::exit (fn [] (throw (ex-info "exit" {}))))

(binding [*restarts* (merge *restarts*
                            #:user{:exit
                                   (fn [args__14292__auto__]
                                     (make-jump
                                      :semaphore.core/jump-target14766
                                      args__14292__auto__))})]
  (try
    (analyze-logs
     '("LOG: Hello, world!"
       "LOG: This is a second log entry"
       "LOG: "
       "LOG: hey"
       "blah"
       "ERROR: "))
    (catch
        semaphore.signal.Signal
        e__14300__auto__
      (apply
       (condp #(semaphore.proto/is-target? %2 %1) e__14300__auto__
         :semaphore.core/jump-target14766 (fn 
                                            []
                                            (throw
                                             (ex-info "exit" {})))
         (throw e__14300__auto__))
       (semaphore.proto/args e__14300__auto__)))))

2021-04-01T01:02:16.176800Z

Sent in a reply so as not to put a lot of code inline

2021-04-01T01:02:26.176900Z

Maybe you are constructing something like a lazy seq which is being realized outside the scope of the binding

2021-04-01T01:02:35.177100Z

No sequences are produced

2021-04-01T01:02:51.177300Z

actually wait

2021-04-01T01:02:54.177500Z

maybe that's it

2021-04-01T01:03:26.177700Z

That was it

2021-04-01T01:03:58.178100Z

No sequences are in the macro etc, but the return value produces a lazy value

2021-04-01T01:04:15.178300Z

Thanks!

2021-04-01T01:05:00.178400Z

Yep

onetom 2021-04-01T02:13:01.178600Z

@emccue im curious too! have u been already familiar with spring sec before u learnt clojure? @vemv let's not forget that one of clojure's core ideas is to allow leveraging existing jvm solutions.

emccue 2021-04-01T02:56:56.178800Z

I'm not going to pretend to have a real scientific justification but in general for stuff like passwords and hashing i'd rather have stuff that is more well audited. Its a comfort blanket sort of thing.

emccue 2021-04-01T03:00:03.179200Z

I tend to program in clojure as if everyone involved in the language other than me is going to die suddenly one day choking on the dust that was once a river

emccue 2021-04-01T03:01:49.179400Z

JDBC stuff is stable, http stack is stable, libraries that work with just data I can alter if needed

emccue 2021-04-01T03:02:25.179600Z

"Security" just feels like something that would require frequent/crucial updates from domain experts. I trust the spring community for that more

emccue 2021-04-01T03:05:17.179900Z

Which is hypocritical, since I do use buddy for stuff

vemv 2021-04-01T03:05:40.180100Z

IDK, a crypto fn is essentially a mathematical function and I don't expect math to change much over time :) FWIW I'm not java-allergic (in fact the opposite) but if I can pick a lib instead of a framework, that seems a win to me auditing is also a delicate topic. A framework (even more so one written in java) seems hard to audit; often surprisingly few people know the guts of the stuff

vemv 2021-04-01T03:06:27.180300Z

it's not all kittens and unicorns in clojure land of course, but a smaller lib in my language of choice seems vastly easier to assess (especially for code coverage)

emccue 2021-04-01T03:07:30.180500Z

> a crypto fn is essentially a mathematical function Yeah, but the time taken to compute that function is an important consideration. I remember reading this a while back https://security.stackexchange.com/questions/18197/why-shouldnt-we-roll-our-own and it i think influences my opinion

emccue 2021-04-01T03:09:04.180800Z

There is this offhand comment in there "a scheme isn't secure until it has been extensively attacked."

🙂 1
emccue 2021-04-01T03:09:37.181Z

and at this point i'm arguing with vibes and impressions, but it does make sense to me

emccue 2021-04-01T03:10:28.181300Z

at least in the manner I use spring security it is a library - it has compile time dependencies on spring but I don't think much at runtime, and I just call the java classes directly without any of that factory/bean nonsense

👍 1
emccue 2021-04-01T03:12:16.181600Z

(def ^PasswordEncoder password-encoder
  (PasswordEncoderFactories/createDelegatingPasswordEncoder))

(defn create!
  "Creates and returns a user record."
  [db email password]
  (sql/insert! db "\"user\"" {:email (string/lower-case email)
                              :password_hash (.encode password-encoder password)}))

(defn confirm-password
  "Takes a user and a password to check to see if it is the password for the user."
  [user password]
  (let [password-hash (:user/password_hash user)]
    (.matches password-encoder password password-hash)))

emccue 2021-04-01T03:12:33.181900Z

(side project, not work project so I can share code)

vemv 2021-04-01T03:15:09.182100Z

I respect all of that :) probably the world would be a safer place if there more lang-independent test suites around for crypto, csrf, etc. Like https://github.com/nst/JSONTestSuite but for this domain Else our security boils down to trust. There isn't a categorical difference between trusting Spring or trusting a high-quality :clj: lib

emccue 2021-04-01T03:18:25.182400Z

Yeah, which - there is a categorical difference between stuff like buddy and https://github.com/jcf/oauth-two

emccue 2021-04-01T03:20:05.182700Z

no offense to jcf or his code. I can't think of any obvious things he would get wrong or can even justify oauth being an issue

emccue 2021-04-01T03:20:48.182900Z

but it is marked as "This project is under active development, and has yet to reach 1.0. As such the API may change." in the readme, has 4 releases, hasn't been updated in 5 years, and has 17000 downloads on clojars

Timofey Sitnikov 2021-04-01T13:22:00.186700Z

Good morning, I have a function that I think produces a lazy sequence:

(defn all-files []
  (let [grammar-matcher (.getPathMatcher 
                          (java.nio.file.FileSystems/getDefault)
                          "glob:*.{txt}")]
    (->> "./resources/"
         <http://clojure.java.io/file|clojure.java.io/file>
         file-seq
         (filter #(.isFile %))
         (filter #(.matches grammar-matcher (.getFileName (.toPath %))))
         (map #(.getAbsolutePath %))))) 
It outputs a sequence of all txt files in the tree. When I begin to use sequence with a map that will read each file and print out the file name, I end up getting the following error:
"/home/my_home/clojure/./resources/data/4/file1.txt"
"/home/my_home/clojure/./resources/data/4/file2.txt"
"/home/my_home/clojure/./resources/data/4/file3.txt"
"/home/my_home/clojure/./resources/data/4/file4.txt"
Execution error (NullPointerException) at (REPL:1).
null
I am assuming that this is because the lazy sequence is not done and when I consume it, it runs out of completed items and then hits the error. Is that a good assumption?

alexmiller 2021-04-01T13:25:51.187300Z

maybe. might want to (clojure.repl/pst *e) when you get it to see the stack trace

alexmiller 2021-04-01T13:26:50.188100Z

(you'll want to be careful reading resources as files too - this won't work if you package this code+resources in a jar)

Timofey Sitnikov 2021-04-01T15:53:37.188400Z

Never did the stack tracing, will try to figure it out, thank you.

awb99 2021-04-01T16:53:31.191500Z

I want to do a couple http/rest api calls. The api endpoint has rate-limiting. I am looking for a good way to structure the workflow. So say I start with 1 api call, then update an atom based on the result, then do more calls. Sometmes I have to iterate throuh paged results, etc. Is there some kind of example with core.async avaiable, where I could see how to structure a workflow like that?

2021-04-01T16:56:14.191600Z

https://github.com/brunoV/throttler the source here is pretty small and worth checking out even if you don't want to use the library

alexmiller 2021-04-01T16:57:48.191900Z

we have some work in progress towards adding something like this to core.async, @ghadi can probably drop a gist

👀 2
awb99 2021-04-01T17:06:13.192300Z

Thanks @jjttjj and @alexmiller

awb99 2021-04-01T17:07:42.192500Z

I am using throttler. But I find thst he orchestration code I have is really bad. In javascript there are a ton of libraries that use promises to structure workflows. AndI would like to write clj + cljs code with core.async that has similar functionality.

awb99 2021-04-01T17:08:00.192700Z

The actual code that does the work is easy to write in clj + cljs.

awb99 2021-04-01T17:08:28.192900Z

But the coordination to structure a workflow is pretty difficult

2021-04-01T19:12:39.193200Z

Iteration is feedback

2021-04-01T20:31:46.193500Z

which is to say, if you have something like clojure.core/iterate, the way it works is it produces a seq by calling a function to get the first element, and then feeding that first element back into the function to get the rest

2021-04-01T20:32:29.193700Z

iteration is what you want for paginated apis, because each page is a page of results + some "next" value that you use to get the next page

2021-04-01T20:33:44.193900Z

with core.async there are sort of two ways to build an interative process

2021-04-01T20:34:55.194700Z

1. take a function and iterate it (lifting a function into a process) 2. take a process and iterate it

2021-04-01T20:36:41.196300Z

iterating a a function to turn it in to a process is pretty straightforward, it looks just like using iterate to build a lazy sequence, but instead of constructing a lazy seq you are sending to a channel

2021-04-01T20:39:15.197800Z

iterating a process involves adding a backward edge (like another channel), that takes data from the output and feeds it back into the input

Max 2021-04-01T20:42:08.199Z

I know anonymous functions with multiple arguments aren’t super popular here, but I hope someone enjoys adding this to their utils namespace:

(defn map-curry [f coll]
  (map #(apply f %) coll))

(map-curry #(hash-map %2 %1}) {:a 1 :b 2})
;; =&gt; ({1 :a} {2 :b})
;; Compare to:
;; (map (fn [[k v]] (hash-map %v %k))  {:a 1 :b 2})

;; Also works on tuples!
(map-curry #(+ % 1 (/ %2 %3)) [[1 2 3] [4 5 6]])
;; =&gt; (8/3 35/6)
EDIT: I got it backwards, it’s actually uncurrying so a better name would be map-uncurry

dpsutton 2021-04-01T20:45:18.199400Z

where does the curry come in?

🍛 1
☝️ 1
awb99 2021-04-01T21:46:09.200400Z

Thanks @hiredman this is what I need. Do you know where I might get some kind of example for this two approaches?

quoll 2021-04-01T21:51:52.205300Z

Seeing the construct #(apply f %) reminds me of a question. Rather than the anonymous fn syntax, I prefer the higher-order-fn approach of (partial apply f). However, that used to come with a performance penalty. I see that https://clojure.atlassian.net/browse/CLJ-1430`partial`, but I thought there was also something about getting it close to anonymous function performance in a release at one point? I’m not seeing it though, so maybe I was wrong. I’m wondering if these two constructs are going to be about the same in performance, or if anonymous functions are still faster. Does anyone know please?

nilern 2021-04-03T07:46:29.227400Z

On Cljs the definitions of stuff like partial and comp also generate a lot of JS. But OTOH if some dependency is using them you will get that JS anyway so why not use them directly too...

2021-04-01T21:53:53.205400Z

I do not. https://github.com/clj-commons/useful/blob/master/src/flatland/useful/seq.clj#L129-L148 is a seq version of unfold(or iterate), you can write something similar that sends output to a channel instead of cons'ing up a seq

2021-04-01T21:55:08.205900Z

in terms of performance the problem is apply not partial ; )

quoll 2021-04-01T21:58:10.206100Z

The question is in terms of: (partial some-fn) vs. #(some-fn %)

quoll 2021-04-01T21:58:51.206300Z

The only reason I mentioned apply was because that was the construct in the above message from Max

seancorfield 2021-04-01T21:58:52.206500Z

I’m influenced by the fact that Rich has said he considers partial to be less idiomatic than an anonymous function.

quoll 2021-04-01T21:59:22.206700Z

I hadn’t heard that. Thank you

2021-04-01T22:01:33.206900Z

(e/qb 1e5
  ((partial tmpfn) nil)
  (#(tmpfn nil)))
;; =&gt; [7.33 4.55]
result in ms

quoll 2021-04-01T22:05:08.207100Z

Do you mean?

(e/qb 1e5
  ((partial tmpfn) nil)
  (#(tmpfn %) nil))

p-himik 2021-04-01T22:07:20.207300Z

An almost verbatim reproduction of the test from that issue:

Clojure 1.10.2
user=&gt; (require '[criterium.core :refer [bench]])
nil


user=&gt; (let [f (partial + 1 1)] (bench (f 1 1)))
Evaluation count : 575498700 in 60 samples of 9591645 calls.
             Execution time mean : 98.891101 ns
    Execution time std-deviation : 1.024939 ns
   Execution time lower quantile : 98.103690 ns ( 2.5%)
   Execution time upper quantile : 100.146501 ns (97.5%)
                   Overhead used : 5.615028 ns

Found 4 outliers in 60 samples (6.6667 %)
	low-severe	 3 (5.0000 %)
	low-mild	 1 (1.6667 %)
 Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
nil


user=&gt; (let [f (fn [a b] (+ 1 1 a b))] (bench (f 1 1)))
Evaluation count : 6352825620 in 60 samples of 105880427 calls.
             Execution time mean : 3.946144 ns
    Execution time std-deviation : 0.466162 ns
   Execution time lower quantile : 3.811868 ns ( 2.5%)
   Execution time upper quantile : 3.993066 ns (97.5%)
                   Overhead used : 5.615028 ns

Found 1 outliers in 60 samples (1.6667 %)
	low-severe	 1 (1.6667 %)
 Variance from outliers : 77.1883 % Variance is severely inflated by outliers
nil

2021-04-01T22:08:13.207700Z

@quoll A silly mistake, but the result is basically identical

p-himik 2021-04-01T22:08:38.207900Z

In my case, the result is quite different. :) 99ns vs 4ns.

quoll 2021-04-01T22:09:24.208100Z

This is why I don’t trust myself benchmarking things like this 🙂

quoll 2021-04-01T22:09:34.208300Z

Thank you for the comparisons

p-himik 2021-04-01T22:10:42.208500Z

I imagine, the results may also vary greatly between JVMs. But I'm too lazy to actually check it. Especially since I almost never use partial myself.

2021-04-01T22:10:43.208700Z

@p-himik probably because you're declaring the function in let, and #{ wastes time declaring the function on the fly with each iteration of the bench

p-himik 2021-04-01T22:11:11.208900Z

That lambda ends up being compiled as a class, once. It's not recompiled on each execution.

➕ 1
p-himik 2021-04-01T22:11:47.209100Z

partial is useful when you have a multi-arity function that you want to curry.