This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-07
Channels
- # architecture (35)
- # babashka (9)
- # beginners (31)
- # biff (15)
- # calva (8)
- # catalyst (3)
- # cider (7)
- # clerk (4)
- # clj-kondo (24)
- # clj-yaml (10)
- # clojure (58)
- # clojure-europe (65)
- # clojure-japan (1)
- # clojure-nl (1)
- # clojure-norway (89)
- # clojure-spec (1)
- # clojure-sweden (1)
- # clojure-uk (8)
- # clojurescript (14)
- # cursive (3)
- # datahike (1)
- # datomic (29)
- # emacs (8)
- # graalvm (20)
- # graphql (1)
- # gratitude (2)
- # helix (6)
- # hyperfiddle (65)
- # jobs-discuss (7)
- # leiningen (1)
- # lsp (6)
- # malli (14)
- # missionary (12)
- # nrepl (8)
- # off-topic (24)
- # polylith (29)
- # reagent (14)
- # sci (14)
- # shadow-cljs (6)
- # spacemacs (10)
- # sql (4)
hey all, question about a macro I’m writing. I’m walking the body of the forms passed to the macro and trying to detect the symbol foo.bar/func
. I’ve noticed that aliases aren’t expanded inside the defmacro, so if the user has aliased foo.bar
to bar
, say, then I’ll see bar/func
.
Does anyone have good prior art for checking if a symbol is either :refer
ed directly or has a namespace that’s aliased?
Ideally this would work in cljs AND clj…
Often, if your macro cares about what a symbol resolves to, then you might want to redesign your macro, but there are some good reasons for it.
The short answer is that you can use resolve
for clojure and https://cljs.github.io/api/cljs.core/resolve for cljs.
The shorter answer is it depends.
The longer answer depends on a bunch of other stuff. Things get tricker if you eventually end up in other environments like self hosted cljs, sci, graalvm native, etc.
I’m with you that this pattern is a warning sign, but I’ve convinced myself that the domain requirements here force me into this…
though I’m still searching for a way around it. basically I want to alter-var-root!
but only for calls to foo.bar/func
inside the body of the macro. and dynamic binding won’t work here because I want the bindings to only apply “one level deep”, and I can’t figure out a way to say
• bind a dynamic variable
• if there are ANY FUNCTION CALLS inside this macro, remove the binding for those calls
@U017QJZ9M7W I can imagine something like this:
(ns foo.bar)
(def ^:dynamic *func-overrides* [])
(defn default-func [args]
<default-func-implementation-here>)
(defn func [args]
(if-let [override (peek *func-overrides*)]
(binding [*func-overrides* (pop *func-overrides*)]
(override args))
(default-func args)))
(defmacro call-func-with-one-level-override [override-fn args]
`(binding [*func-overrides* (conj *func-overrides* ~override-fn)]
(func ~args)))
That is really interesting
I’ll write more soon about the actual problem here, it’s in the implementation of a probabilistic programming language, definitely a cool domain
okay, so here is the problem as currently laid out.
the ideas are:
• I am writing programs that internally can make random choices
• I have a trace!
primitive that makes a random choice, but also stores it in a hidden piece of dynamically bound state
• (simulate f args)
is like (apply f args)
, but sets up the hidden state and the machinery to track the random choices, and returns {:trace <map of random choices> :retval <(apply f args)>}
Here is trace!
, which by default also acts like apply
:
(defn no-op [_k f args]
(apply f args))
(def ^:dynamic *trace* no-op)
(defn trace! [addr f & args]
(*trace* addr f args))
and then simulate
dynamically binds the *trace*
function to capture its results in a map:
(defn simulate [f args]
(let [trace (atom {})]
(binding [*trace* (fn [k f args]
(let [v (:retval (simulate f args))]
(swap! trace assoc k v)
v))]
(let [retval (apply f args)]
{:trace @trace
:retval retval}))))
Then we can add this into the pot to turn off tracing:
(defn untraced [f]
(binding [*trace* no-op]
(f)))
As an example:
(letfn [(f []
(if (trace! :k rand-int 10)
1
2))]
(simulate f []))
;;=> {:trace {:k 0}, :retval 1}
So here is the trouble. The current spec wants to say that if I simply CALL a function inside of f
, then tracing should be turned off; a function call should implicitly insert an untraced
wrapper:
(letfn [(f []
(ignore)
(if (trace! :k rand-int 10)
1
2))]
(simulate f []))
If the user wants tracing, they should have to write:
(letfn [(f []
(trace! :v ignore)
(if (trace! :k rand-int 10)
1
2))]
(simulate f []))
(in the original language there is a splice!
version which doesn’t introduce another level of nesting)what I can’t figure out is how I can possibly have the call to ignore
not trigger any bindings.
the way the original code did it, which led to my macro, was that trace!
was a no-op, and the instead of dynamically binding, the language used a version of fn
called gen
that structurally rewrote any references to trace!
into calls to *trace*
. that’s why I cared what a symbol resolved to within the macro
anyone know off hand how to get a ring/jetty response to not do http chunking and just inline the payload?
I am not sure of the internal mechanics of the ring jetty adapter, but basically if you hand it something as the body that it can't know the length of a head of time (some kind of stream) it has to chunk
The http protocol essentially requires you to either set a content-length header or use a transfer encoding like chunked. I vaguely recall looking at this, but the details escape me, but somewhere between the ring jetty adapter and jetty, some kinds of bodies it can automatically figure out the length of, and if it can't, you get chunking
The most recent version ring-jetty9-adapter (jetty 12.x) should buffer up to 2GB by default, there's more info about it here https://github.com/sunng87/ring-jetty9-adapter/pull/106#issuecomment-1705491599 @U0NCTKEV8 summed it up.
correction: rather its 2GB by default \w vanilla jetty 12.x, \w with ring-jetty9-adapter the buffer size limit is 16kb by default https://github.com/sunng87/ring-jetty9-adapter/commit/31173a0a4d2459e570d1f25a463f24bd29930af7#diff-e7a14ad839fd5d6b64052aab9c40a7c023ead62def1afecac23199b1c65d7965R92 there's no published jar yet, so just use HEAD / git dependency
anyone else have failing GitHub workflow runs that use DeLaGuardo/setup-clojure action? Mine started failing today (HTTP 404), I’m wondering if this is some github problem
looking at the repo for the action, it could also be this https://github.com/DeLaGuardo/setup-clojure/commit/15c9fc87714c6d88c5085249d41ea722b548b2b5
I know he was shifting to use the new download location for the CLI, would probably be helpful for you to drop an issue on the project if something not working
http://Practical.li/blog uses cryogen to generate the blog website and GitHub pages to host. PRs are published to a staging site (using a small cryogen customisation) for peer review and commits & PR merges are published live, all via GitHub workflows https://github.com/practicalli/blog
Another +1 for Cryogen + GitHub pages. That's what powers http://clojure-doc.org (and http://corfield.org).
this is also an option https://github.com/borkdude/quickblog
New here, so sorry if I'm in the wrong channel: Has anyone here used VisualVM to profile clojure? Have you had any success doing this?
A post I read a while ago that shows successful usage: https://yogthos.net/posts/2012-08-21-Reflecting-on-performance.html
1. Do I need to launch my clojure app with any flags to allow VisualVM to connect? 2. Can I find my package names as they are defined in clojure or are they prepending with something.
1. I don't remember myself but a Clojure app is just a Java app with extra things on top. Whatever you have to do to make VisualVM work with a Java app, you also have to do with your Clojure app. The ways of doing that might differ though and depend on whether you're using Lein or tools.deps
2. Clojure constructs end up being Java classes. Nothing will be prepended but things will get munge
d. E.g. clojure.core/+
is clojure.core$_PLUS_
on the JVM, and that's how VisualVM will show it
so I should be able to find my class if defined as com.mystuff
as just that in visualvm
also, I have two apps listed as clojure. I'm guessing one is the repl and one is my actual app, is that what other people have seen?
Note also there are Clojure tools for profiling, such as https://clojure-goes-fast.com/. In particular, https://github.com/clojure-goes-fast/clj-async-profiler. That's the one I use myself most often.
I'll take a look at these tools
> I have two apps listed as clojure
Again - depends on your tools, how you use them, and maybe even your code (who knows, maybe you or some libraries are shelling out to clojure
)
If you prefer VisualVM, you can check this article: https://clojure-goes-fast.com/blog/profiling-tool-jvisualvm/
But I also suggest clj-async-profiler: https://clojure-goes-fast.com/kb/profiling/clj-async-profiler/
Two Clojure apps being shown is most likely because you use Leiningen (it spawns one process for itself and one for your app). The blogpost I linked mentions it
that's the second recomendation for clj-async-profiler, so I'm adding it to my ticket for things to look into
yea, I think leinigen is one of them, so I'm going to guess it's the one with the lower pid
Type jcmd
in the terminal and it will show you the command lines of all active Java processes. That will help you distinguish which process is which
> I'm going to guess it's the one with the lower pid (edited) That's a good heuristic but might not work sometimes, OS can reuse pids
since most clojure projects look the same at first glance on visualVM I add to each project a jvm option like "-Dproject-name=MyProjectName"
so then is easy to just figure out what project you are looking at from the first visualvm tab
You can do it on your lein project.clj by adding :jvm-opts ["-Dproject-name=MyProjectName"]
I use VisualVM quite a lot with Clojure but just for Heap monitoring and quick threads and heap dumps, not much for profiling, I find clj-async-profiler much better at it
@U0739PUFQ If it's only for VisualVM, you can use -Dvisualvm.display.name=FooBar
instead - it will display the passed name right in the list of applications.
amazing, thanks @U2FRKM4TW