clojure

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

Could someone point me in the direction of how I would [with clojure] read in a .yaml modify it programmatically, then write out the modified version back to a .yaml?

steffan 2021-01-17T16:50:45.168100Z

Note that the maintained fork of clj-yaml is at https://github.com/clj-commons/clj-yaml I have an open pull request on Clojure Toolbox to update similar URLs.

2021-01-17T02:03:32.153300Z

I have not used either of the two libraries mentioned under the "YAML" category on the Clojure Toolbox page, but I would start by checking out the readme docs for those two libs: https://www.clojure-toolbox.com/

orpheus 2021-01-17T02:06:16.153600Z

Thank you!

burbma 2021-01-17T04:53:22.155800Z

I just put a question on stack overflow (the code snippet seemed too long for slack). It's about transducer performance with and without using an async/chan. https://stackoverflow.com/questions/65757419/understanding-clojure-transducer-performance.

burbma 2021-01-19T02:31:11.235900Z

Thank you for these interesting investigations!

phronmophobic 2021-01-17T05:35:52.156Z

it doesn't exactly answer your question, but maybe helps shed some light. chan-xf and chan-then-nested aren't necessarily analogous since chan-xf is running the transducer before putting data on the channel while chan-then-nested is running the transducer after taking data off the channel. I think the following is a better comparison and chan-xf is about twice as fast on my computer:

(defn chan-then-nested []
  (let [c (async/chan n)]
    (async/onto-chan c (tx data))
    (while (async/<!! c))))

(defn chan-xf []
  (let [c (async/chan n xf)]
    (async/onto-chan c data)
    (while (async/<!! c))))

phronmophobic 2021-01-17T05:36:50.156300Z

but it still doesn't explain your other example's result

phronmophobic 2021-01-17T05:37:42.156500Z

if I was feeling a little more industrious, I might try to run your examples with https://github.com/clojure-goes-fast/clj-async-profiler

phronmophobic 2021-01-17T05:45:53.156800Z

interesting. there seems to be a big difference between running the transformation before and running it after:

(defn chan-then-nested-before []
  (let [c (async/chan n)]
    (async/onto-chan c (tx data))
    (->> c
         (async/into [])
         async/<!!
         last)))

(defn chan-then-nested-after []
  (let [c (async/chan n)]
    (async/onto-chan c data)
    (->> c
         (async/into [])
         async/<!!
         tx
         last)))

phronmophobic 2021-01-17T05:46:36.157Z

chan-then-before-before is about 1.5-2x slower then chan-then-nested-after

2021-01-17T06:02:04.157500Z

Could it be that the transducer on the chan is just slow enough that it makes coordination between the two go loops (onto chan and a/into) more difficult and add more overhead? So in the tx version there are several (most?) items buffered by the time the takes are attempted, but with the xf version there is more waiting for a values to be ready by the async/into go block?

2021-01-17T06:15:01.157700Z

Like I could imagine that interleaving the producer and consumer go blocks would add more overhead then just filling a buffer then subsequently draining it

phronmophobic 2021-01-17T06:16:51.157900Z

yea, something weird with the interleaving:

(defn chan-then-nested-before []
  (let [c (async/chan n)]
    (async/onto-chan c (tx data))
    (->> c
         (async/into [])
         async/<!!
         last)))


(defn chan-then-nested-before-doall []
  (let [c (async/chan n)]
    (async/onto-chan c (doall (tx data)))
    (->> c
         (async/into [])
         async/<!!
         last)))
chan-then-nested-before-doall is significantly faster than chan-then-nested-before

Alex 2021-01-17T07:01:22.158800Z

Has anyone used Clojure (JVM, not Clojurescript) on serverless Google Cloud functions?

zendevil 2021-01-17T11:10:07.160200Z

I’m getting an error when doing a post request that I’m not getting when doing a get request

zendevil 2021-01-17T11:10:18.160400Z

here are my reitit routes

zendevil 2021-01-17T11:10:38.160800Z

(defn home-routes [] 
[""
   {:middleware [middleware/wrap-csrf
                 middleware/wrap-formats]}
   ["/" home-page]
   ["/api"
    ["/sign-in" sign-in]
    ["/upload-video" upload-video]]])

zendevil 2021-01-17T11:11:14.161100Z

here’s my get and post request functions:

(defn http-get [uri params on-success on-failure]
  {:method :get
   :uri (str "<http://localhost:3000>" uri)
   :params params
   :on-success on-success
   :on-failure on-failure
   :response-format (edn/edn-response-format)
   :format (edn/edn-request-format)
   })

(defn http-post [uri params on-success on-failure]
  {:method :post
   :uri (str "<http://localhost:3000>" uri)
   :params params
   :on-success on-success
   :on-failure on-failure
   :response-format (edn/edn-response-format)
   :format (edn/edn-request-format)
   })

zendevil 2021-01-17T11:11:21.161400Z

and here’s how the functions are being called:

zendevil 2021-01-17T11:11:43.161700Z

(reg-event-fx
 :upload-video
 (fn [coeffects _]
   {:http-xhrio (http-post "/api/upload-video" {}
                           [:upload-success]
                           [:upload-error])}))

zendevil 2021-01-17T11:12:38.162200Z

I’m getting the following response:

zendevil 2021-01-17T11:12:40.162500Z

{:response &lt;!DOCTYPE, :last-method "POST", :last-error "undefined [403]", :failure :error, :status-text nil, :status 403, :uri "<http://localhost:3000/api/upload-video>", :debug-message "Http response at 400 or 500 level", :last-error-code 6}

zendevil 2021-01-17T11:12:47.162700Z

how to fix this error?

p-himik 2021-01-17T11:44:30.163200Z

There's not nearly enough code here to be certain, but I'm going to guess it has something to do with you using middleware/wrap-csrf and not sending the CSRF token.

takis_ 2021-01-17T15:40:37.165600Z

Hello is it possible to do something like the bellow? myns contains many clojure.core names that overrides

(:use myns :exclude clojure.core)

takis_ 2021-01-17T15:42:22.166800Z

I tried 2 things that worked but maybe better ways

(def excluded-core-names '[get let ..])

(use `[myns :exclude ~excluded-core-names])

takis_ 2021-01-17T15:42:40.167300Z

(:refer-clojure :only [])
(:use myns)
(:refer-clojure)

vemv 2021-01-17T16:58:08.168600Z

(:refer-clojure :only []) works. Seems pretty clean. TIL! (I deleted something I wrote earlier)

Joe 2021-01-17T17:46:10.170600Z

Is it weird that (conj) returns a vector [], but (conj nil 1) returns a list (1) , or am I overthinking it?

takis_ 2021-01-17T17:52:47.170700Z

yes , its fine , i just left it like that 🙂 thanks

2021-01-17T18:02:15.170900Z

In Clojure nil is not a collection of any type, so it is a design choice which collection type to return in both of those cases.

2021-01-17T18:02:31.171100Z

You can always control the return type of the collection by providing an actual collection to conj onto, and avoid both of those kinds of calls.

Joe 2021-01-17T18:03:23.171300Z

> so it is a design choice which collection type to return in both of those cases. But in these two cases the choice was different for each. Just wondering why that was

2021-01-17T18:04:34.171700Z

Understood. They both seem like easily avoidable cases by the way you call conj, if it bugs you, so should be irrelevant in typical Clojure programs.

2021-01-17T18:05:14.171900Z

I don't have any knowledge of whether there is some rationale given for that design choice, sorry.

Joe 2021-01-17T18:06:37.172100Z

Yeah, it's definitely not a big thing in practical terms. Thanks!

2021-01-17T18:16:46.172300Z

Both of those design choices seem to have been consistent since Clojure was originally published, circa 2006

p-himik 2021-01-17T19:06:52.174100Z

The very same question has been asked just 9 days ago - Slack hasn't removed it yet: https://clojurians.slack.com/archives/C03S1KBA2/p1610095555367300

👍 2
p-himik 2021-01-17T19:07:20.174400Z

There's a thread with good explanations under that message.

2021-01-17T19:44:38.174800Z

Maybe I should make an http://ask.clojire.org quetion/answer out of it

Joe 2021-01-17T19:54:39.175Z

> The very same question has been asked just 9 days ago so it was, thanks! Even exactly the same thought process: the update use case was what got me thinking about the (conj nil 1) thing. Though I'd say even after reading it I'm at the same place as didibus - unclear as to whether the difference is due to an intentional design decision or not.

p-himik 2021-01-17T20:00:51.175200Z

conj by default uses [] in its 0-arity. Treating nil as a sequence yields a list. No matter whether you're using conj or into or rest or something else. These two things are completely orthogonal.

👍 1
💡 1
2021-01-17T20:13:07.175800Z

I think it is due to an intentional decision, but made at different times for different reasons.

2021-01-17T20:15:23.176100Z

Initially conj only had a 2-arity: (conj nil 1) or (conj [1 2] 3) or (conj '() 1). This was in the time of sequences, and conj was exclusively a sequence function. From that perspective, the question was, what should (conj nil 1) return? And the logical answer was, we should probably treat nil as the empty sequence, so nil should default to an empty seq and thus (conj nil 1) ;=&gt; (1) was made to return a seq.

p-himik 2021-01-17T20:19:26.176300Z

IMO, as I said, lists are not about conj at all:

user=&gt; (type (conj nil 1))
clojure.lang.PersistentList
user=&gt; (type (into nil [1]))
clojure.lang.PersistentList
user=&gt; (type (rest nil))
clojure.lang.PersistentList$EmptyList
user=&gt; (type (reverse nil))
clojure.lang.PersistentList$EmptyList
user=&gt; (type (dedupe nil))
clojure.lang.PersistentList$EmptyList

p-himik 2021-01-17T20:20:56.176500Z

So the initial question (if there even was one) was probably "what kind of collection should nil beget" and not "what should (conj nil whatever) return".

2021-01-17T20:24:36.176700Z

Later, conj was evolves to also be a transducer, which meant that a 0-arity and a 1-arity were added to it. Transducers are conceptually collections functions, not sequence functions. The 1-ary in transducers is the completion function, it says what to do at the end of applying a transducer, and in the case of conj we don't need to do anything except return the result, thus the 1-ary of conj just returns its argument as is, aka its an identity function. Now the 0-ary in transducers is the init function, this will be called when there is no starting collection in order to decide what the default is, and it could be used to setup some other things related to the particular transducer. Now the question here was again, what should be the default when conj is used in a transducer with no starting collection specified? In this case it was decided that would be vector, and this is because transducers are conceptually collection functions, so defaulting to a sequence wouldn't make sense. Now you could ask why it didn't default to list, and I think that is because vectors are a nicer default here since they don't reverse the order of the elements when processed. Now when (conj nil 1) was chosen, you could say hey but this was made a list and that reverses the order so what gives? Well, the rationale was different, for that it was about treating nil and empty seq as equivalent, since an empty seq is a list it returns a list. Where as for transducers it wasn't about that, it was about what is a good init collection for transducing over conj if none are provided, and here vector was chosen as the best one to pick from.

2021-01-17T20:30:45.176900Z

> So the initial question (if there even was one) was probably "what kind of collection should nil beget" and not "what should (conj nil whatever) return". That seems the same question to me?

2021-01-17T20:40:20.177200Z

Hum.. actually conj was never a sequence function hum... Might need to revise my answer a bit. Though I think it was the same idea, nil is often treated same as empty list, so I think that was the idea for conj as well

2021-01-17T20:56:06.177500Z

conj has always worked to add a new element to any kind of collection, not only lists or sequences. It worked on sets, maps, and vectors from the beginning.

2021-01-17T20:57:07.177700Z

nil being the empty list is a Common Lisp-ism, one that Rich explicitly rejected as a design decision for Clojure in most places -- mentioned in his talk on Clojure for Lisp programmers. In Clojure () is the empty list, not nil. nil is not a collection.

2021-01-17T21:14:25.177900Z

"But nil means nothing in Clojure. It means you have nothing. It does not mean some magic name that also means the empty list. Because there is empty vectors, and there is empty all kinds of things. "nil" means you do not have something. So either you have it, a sequence, or you have nothing, nil. If you have a sequence, it is going to support these two functions." part of this talk: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureIntroForLispProgrammers.md

1
2021-01-17T21:19:09.178400Z

Also if you search for "eos" in that transcript, there is a slide where he talks about nil vs. empty lists and a few other things, comparing what Common Lisp, Scheme, and Clojure do there.

p-himik 2021-01-17T21:42:35.180100Z

If I want to cache a single value forever, is it OK to use something like:

(def cached-value (atom nil))

(defn compute [args]
  (or @cached-value (reset! cached-value (real-compute args))))
(assuming the result is never false so the use of or is safe)

p-himik 2021-01-17T21:43:19.180200Z

Forgot to specify - real-compute is pure. I don't care how many times it ends up being called in the end.

2021-01-17T21:44:23.180400Z

Why not just have compute wrap real-compute with memoize?

2021-01-17T21:44:40.180700Z

I can't think of any reason that would be unsafe. You can also use delay instead of an atom.

2021-01-17T21:46:13.180900Z

In your compute function, if you ever call compute with different args, that cause real-compute to be called with those different args, and return different results, then you have a race which of those results ends up being stored in the atom.

2021-01-17T21:46:35.181100Z

(you have a race if you call real-compute from multiple threads, at least)

p-himik 2021-01-17T21:46:47.181300Z

Thanks, right! Apparently it was delay that induced that itch that made me ask the question in the first place. @robert.mitchell36 Because args are costly to compare and I don't care if they're different. I only care about the very first time it's called.

👍 1
p-himik 2021-01-17T21:49:20.181700Z

Ah, bummer - delay needs to create a closure and it needs to be stored somewhere. atom it is then.

2021-01-17T21:49:22.181900Z

In that case, you could also use a promise.

👍 2
2021-01-17T21:51:18.182200Z

Something like:

(def first-value (promise))
(defn compute [args]
  (if (realized? first-value)
      @first-value
      (deliver first-value (real-compute args))))

2021-01-17T21:52:40.182500Z

> conj by default uses `[]` in its 0-arity. > Treating `nil` as a sequence yields a list¹. Thankyou @p-himik, I finally see how this could be considered consistent! 🙂 ¹ emph. added

👍 1
p-himik 2021-01-17T21:52:42.182700Z

A small correction to your code - seems like deliver returns the promise itself, so it also needs @.

2021-01-17T21:52:57.182900Z

Ah, right. Good catch!

p-himik 2021-01-17T21:53:20.183200Z

Ah, and it can return nil. So it's not really thread-safe, I think.

p-himik 2021-01-17T21:54:05.183500Z

And promise is just a wrapper on top of atom. :D OK, I'll definitely stick with atom.

Joe 2021-01-17T21:56:24.183800Z

Interesting stuff, thanks for discussion and links!

➕ 1
p-himik 2021-01-17T21:56:26.184Z

@andy.fingerhut What does "eos" stand for? Or what does it mean?

2021-01-17T21:58:07.185500Z

I haven’t read to confirm in detail, but IIRC in this context it means “end of sequence”

p-himik 2021-01-17T22:01:07.185700Z

Thanks!

2021-01-17T22:22:45.186600Z

I think I'll put down conj as "practical, and probably consistent with implementation/usage/history while not necessarily formally self-consistent as in having simplest possible behaviour" until some future moment of enlightment 🙂

2021-01-17T22:36:26.186800Z

I think real-compute might get called twice if your app is multithreaded. Maybe something like this would be better: (defn compute [args] (swap! cached-value (fn [v] (or v (real-compute args)))))

2021-01-17T22:55:37.187Z

Just don't call conj with 0 args, or a first arg of nil, and these corner cases are unimportant to you.

✔️ 1
2021-01-17T23:17:58.187400Z

When you say "it can return nil, so it's not really thread-safe, I think", are you referring to promises?

p-himik 2021-01-17T23:34:54.187600Z

@jkrasnay As I mentioned - I don't care if it's called multiple times. But your code snippet makes sense, thanks! @andy.fingerhut Yes, promises. deliver can return nil.