This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-26
Channels
- # announcements (28)
- # asami (13)
- # babashka (10)
- # beginners (170)
- # boot (1)
- # calva (35)
- # cider (21)
- # circleci (13)
- # clara (6)
- # clj-http (1)
- # clj-kondo (29)
- # cljdoc (5)
- # clojure (89)
- # clojure-czech (2)
- # clojure-europe (20)
- # clojure-france (16)
- # clojure-nl (6)
- # clojure-uk (5)
- # clojurescript (80)
- # community-development (6)
- # conjure (13)
- # cursive (18)
- # datascript (9)
- # datomic (1)
- # duct (1)
- # gratitude (2)
- # helix (7)
- # jobs (2)
- # kaocha (3)
- # lsp (22)
- # malli (5)
- # meander (1)
- # other-languages (34)
- # pathom (18)
- # polylith (24)
- # quil (10)
- # re-frame (5)
- # releases (1)
- # remote-jobs (4)
- # reveal (7)
- # shadow-cljs (8)
- # tools-deps (53)
Does anybody here use `bat-test` from Metosin? I'm trying to figure out how to stop the capture of unit test output.
It is quite likely no one is using it. The output capture is implemented by eftest: https://github.com/weavejester/eftest#output-capturing But I don't remember how those options are passed in with bat-test, or if there is a support. Looks like bat-test impl passes the whole opts map to eftest, with some changes, but e.g. from boot task there probably isn't way to add extra options properties.
OK, thanks.
Kind of surprised to see Clojure beat Go at making 500 concurrent http get requests: https://gist.github.com/didibus/543076be0499c1256024dee5853ad592
I'm kinda surprised http://clojure.org just allows 500 x n fast as possible requests. Is it returning 200 response codes every time? Not sure how/if error responses would effect performance though
Are you sure that keepalive might not come into play in this case. It seems to be enabled by default for http-kit https://http-kit.github.io/client.html
http://clojure.org is static files served on cloudfront so you're at the whim of whatever edge cache you're hitting. why http and not https? former should be redirecting to the latter.
also keep in mind that we're paying for each of those requests :)
https://reqres.in/ is perhaps a better endpoint to benchmark with.
Yeah I couldn't come up with a wording for "it might not be the best etiquette" that captured the magnitude of the etiquette violation I wanted to convey
Sorry @U064X3EF3 I should switch it to http://golang.org and make El Goog pay instead haha
I mean, it's pennies, but maybe :)
https://golang.org Clojure gets 451ms vs 1.864s on GO
Tried to set Go to use HTTP/1.1 only as well, since it was also using HTTP/2 and http-kit doesn't, but I get similar results either way.
@U0K064KQV Sounds like a good story for Hacker News.
Will a (reset! my-atom myval)
continue until successful? Or might it stop trying after a while?
Unless I missed sth more obvious, it looks like atoms ultimately delegate to https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/sun/misc/Unsafe.java#L863-L865 (via AtomicReference) not sure of how to jump from Java code to C code :)
I don't think volatile == CAS, for one thing they're different concepts/mechanisms in Java
else clojure's Atom would look a lot more like clojure's own volatile!
I'm using reset!
from a child process to send the result back to the main process so the main process can handle the result. I want to be sure the result gets handled eventually.
@U0232JK38BZ you can be damn sure reset!
will eventually succeed, unless perhaps you intentionally place a scenario of extreme contention
I thought this was more of a 'fun' theoretical question
@U053XQP4S you're right, I was chasing the path for swap!
not reset!
(the question made me think of retries which brought swap!
to mind)
But will every reset!
be handled by the watcher that watches the atom that is reset?
Looks like it https://github.com/clojure/clojure/blob/b8132f92f3c3862aa6cdd8a72e4e74802a63f673/src/jvm/clojure/lang/Atom.java#L153-L159 , there isn't branching etc
The <String>
part does not exist in runtime or during compilation. Just List
should be enough.
How do people like to determine when a function should return nil
as opposed to an empty coll
? TMK it’s about choosing a return type and remaining consistent about it. Perhaps there is more to it though? :thinking_face:
In my experience, nil and an empty collection are usually the same, semantically speaking, so I don't bother with consistency in this particular case.
Meh, I worded that poorly... I meant they're semantically the same as output types, not in every possible scenario. As in, should it matter whether filter with no matches gave back nil vs an empty collection? I don't think so
false/nil is like -1 to me [] is like 0 to me everything else is like a positive integer to me
Ι don't get the analogy. Can you elaborate?
It seems to me add-libs still does not exist in tools.deps.alpha ? How can I use it?
You probably need the add-lib3 branch: https://github.com/clojure/tools.deps.alpha/tree/add-lib3
Until it is you can use the branch as a git dependency
@UGC0NEP4Y see here: https://github.com/seancorfield/dot-clojure/blob/3113dbde7e07d9ad45828259c29b58e4af79430e/deps.edn#L93
I have an add-libs in my deps.edn too, it works fine, just just have to require in the namespace
Not sure how the “if we’re starting other processes via aliases ;; such as a socket REPL or Cognitect’s REBL etc” actually means.
The core issue remaining is related to the dynamic classloader you need to make it work correct?
do you use it just to try stuff, or do you later add those libs to your deps.edn?
Both, i.e. there are times when I try some lib to see if it works for me, and if it is, I'll add it to deps.edn; and there are times when it's for a throwaway code that gets deleted
ditto! Very useful for trying things out, then if the experiment works, to incorporate it into my deps.edn
Actually, I usually add lib to deps.edn and then call add-libs with that lib, so it's both available at the repl without restart and cursive indexes it to provide autocomplete
one thing we've wondered about are ratio of "temp experiment" to "know you need" and for the latter, whether it would be useful to have • runtime libs -> persist to deps.edn or • sync runtime from deps.edn (so you'd modify deps.edn, then sync) options instead
lots of thorny questions in both cases but just thinking about workflows
That's a great insight into the thoughts around the situation 🙂 Thanks for sharing.
Don't ever quote me crediting CL, but the ability to interactively "freeze" or image those dependencies could be useful, i.e. the relationship between deps.edn and the runtime can be bidirectional
this is generally, very hard to represent. any particular classpath for a project is created from a set of root deps, optionally some aliases that modify that, and then many transitive deps. the versions selected are dependent on that set of deps at classpath calculation time. adding new libs at runtime as new top-level deps is a best effort exercise (you can't "upgrade" or change a version of something you've already included, and you are adding that new dep and its transitive deps in the context of a set of "pinned" versions for all transitive deps. there are lots of ways to get different answers than if you'd started fresh and/or invalid combinations.
that sloppiness is probably ok if you mostly understand your deps tree and and how this can go wrong (if you add new deps enough times, you will eventually fall over), but that is hard to communicate to a broad range of users. you also want to avoid leading people into this as a way to start an app, so it should be a "repl-only" thing
wait, I still didn't get to the point where you replace git with a series of edits in a graph database 🙂
maybe we should call the function try-lib
instead or something to indicate it's potential for failure :)
Hello macro friends!
I'd like to write a macro called go-inst
that works like a go
macro with a tweak: calls to <!
>!
and alt!
and alts!
need to be wrapped in a piece of code.
For instance:
(go-inst {:context "my-context"}
(let [c (chan)]
(>! c :data)
(<! (chan))))
Should be macro expanded to:
(go
(let ([c (chan)]
(do
(println "entering my-context")
(let [val (>! c :data)]
(println "leaving my-context")
val))
(do
(println "entering my-context")
(let [val (<! c)]
(println "leaving my-context")
val)))))
I need also to support nested go-inst
with different contexts!
My question is: what's the simplest way to write the code for this macro?
Should I use clojure.walk
or clojure.zip
? Why?
I would think just a normal recursive function, since that would make it easy to control your context.
I would use clojure.zip
along with following as the zipper:
(defn clj-zip [obj]
(z/zipper #(and (seqable? %)
(not (string? %)))
seq
(fn [node children]
(let [children (remove #{::delete} children)]
(if (map-entry? node)
(vec children)
(into (empty node) children))))
obj))
you can then walk the tree with something like:
(loop [zip (clj-zip code)
ctx {}]
(if (z/end? zip)
(z/root zip)
(recur (if (something? ctx (z/node zip)
(-> (z/edit zip f)
z/next)
(z/next zip))
ctx)))
It might be overkill for your example, but it's a general approach that has worked for me in the past.Why zip is better than walk for my use case?
hmmm, it might not be. If it's likely that you'll extend it in the near future, then it might
I never thought of using zip for that. But I guess it lets you go up/down, left/right the tree. I might have to try it on a macro in the future
If you need to accumulate context as you walk up and down the tree, then it's easier with zippers
I've seen people do it with prewalk/postwalk and atoms, but I prefer the zipper approach
Right, but then you can't use information from parent nodes, only child branches. For analyzing code, the context is usually provided by the parent nodes.
In my case I definitely need the context
Can you elaborate? Seems like you just need to know the {:context ...}
map, which is a different kind of context - you will know it just because go-inst
is something you control.
I'm 95% certain regular walk
will work wonderfully in your case given that you seemingly need to just replace a particular form with a wrapped version of the same form.
With walk, I'll need to find a way not to wrap with both contexts the <!
of the inner go-inst
. With zip it seems that it will work like this with no effort
But I might be wrong
That's only a problem with prewalk, but you can just use clojure.walk/postwalk
(assuming I understand your comment correctly).
If I use postwalk I won't be able to differentiate between forms that were part of go-inst
block and rewritten as a go
block and forms that were orginally part of of go
block
Not sure I follow. I might need an example. Wouldn't the following work?
(defn go-inst* [ctx body]
(clojure.walk/postwalk (fn [form]
(if (and (list? form)
(#{'<! '>!} (first form)))
`(do
(println ~(str "entering " (:context ctx)))
(let [val# (~(first form) [email protected](rest form))]
(println ~(str "leaving " (:context ctx)))
val#))
form))
body))
(defmacro go-inst [ctx & body]
`(go
[email protected](go-inst* ctx body)))
Let me clarfify what I mean with an example:
(go-inst {:context "my-context"}
(let [c (chan)]
(>! c :data)
(<! (chan)))
(go-inst {:context "your-context"}
(let [c (chan)]
(>! c :data)
(<! (chan)))) )
Is macro expanded to:
(clojure.core.async/go
(let
[c (chan)]
(do
(clojure.core/println "entering my-context")
(clojure.core/let
[val__12648__auto__ (>! c :data)]
(clojure.core/println "leaving my-context")
val__12648__auto__))
(do
(clojure.core/println "entering my-context")
(clojure.core/let
[val__12648__auto__ (<! (chan))]
(clojure.core/println "leaving my-context")
val__12648__auto__)))
(go-inst
{:context "your-context"}
(let
[c (chan)]
(do
(clojure.core/println "entering my-context")
(clojure.core/let
[val__12648__auto__ (>! c :data)]
(clojure.core/println "leaving my-context")
val__12648__auto__))
(do
(clojure.core/println "entering my-context")
(clojure.core/let
[val__12648__auto__ (<! (chan))]
(clojure.core/println "leaving my-context")
val__12648__auto__)))))
While it should be expanded to
(clojure.core.async/go
(let
[c (chan)]
(do
(clojure.core/println "entering my-context")
(clojure.core/let
[val__12648__auto__ (>! c :data)]
(clojure.core/println "leaving my-context")
val__12648__auto__))
(do
(clojure.core/println "entering your-context")
(clojure.core/let
[val__12648__auto__ (<! (chan))]
(clojure.core/println "leaving your-context")
val__12648__auto__))))
If you need nesting where inner go-inst
completely supersedes the outer one, I think you can just leave any calls to go-inst
in your postwalk function as they are.
If you need to be able to replace just some parameters (assuming there can be more than just :context
), then you can change the go-inst
form in your postwalk function so its argument is (merge outer-params inner-params)
.
How can I leave any calls to `go-inst` in my postwalk function as they are?
hmmm. I need to think about it
I'd like a sanity check: there's no stdlib function that does this, right?
(defn- update-some
[m k f & args]
(if-some [res (apply f (get m k) args)]
(assoc m k res)
(dissoc m k)))