This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-09-23
Channels
- # babashka (2)
- # babashka-sci-dev (401)
- # beginners (10)
- # biff (6)
- # calva (6)
- # clj-on-windows (6)
- # cljfx (13)
- # clojure (91)
- # clojure-austin (1)
- # clojure-europe (6)
- # clojure-norway (11)
- # clojurescript (14)
- # clr (3)
- # docker (3)
- # emacs (2)
- # fulcro (21)
- # hyperfiddle (2)
- # membrane (30)
- # nbb (4)
- # pedestal (7)
- # polylith (16)
- # reitit (1)
- # sci (4)
- # xtdb (9)
has someone devised a way to add ‘middleware’ or ‘advice’ to function definitions in a manner that doesn’t scare you and you’d want to keep it in your non-dev builds. i’m running into a lot of use cases where i’d like to instrument certain functions with telemetry, metrics, logging, extra specs, etc, ideally through a simple piece of metadata ^:with-telemetry , or perhaps a new defn macro. alter-var-root and defadvice both are not very good abstractions for this IMO , i know there are libraries like that.
For what it's worth, I've done something like what you describe rewriting my own defn
inspired by the original one
But it's not polished enough for prod, among other things because it does quite some extra stuff and slows down function execution (not massively, but once you start making lots of call it quickly adds up and becomes noticeable)
yah, exactly, it’s not hard to write /a/ solutions here, but i’m somewhat cautious, i want performance, composability and several characteristics.
i use this in dev builds, very emacs-like https://github.com/thunknyc/richelieu
i think the pluggable ‘middleware’ here should run at macro expansion time of ‘defn’ or a defn-like macro, and alter function definition if applicable
not immediately helpful to you, but my more complex programs are more pathom than clojure and its plugin system addresses all these concerns
i really like pathom/eql but i’m stuck on REST and GraphQL at work , due to corporate constraints
We have a defn-with-tracing macro that calls defn normally and then calls alter-var-root to wrap the original function in a binding call that sets some telemetry dynamic vars. Works well for us
A library that I know of, its approach seems reasonable: https://github.com/galdre/morphe

Counter-opinion: if you control the code that you want to cover with extra functionality, then nothing beats a macro that wraps the body. Direct, explicit, flexible. You'll appreciate it the moment when you'll need a slightly more complicated behavior (e.g. "write to this metric if successful or to this if failed"). I guess it won't work for adding extra specs, but for everything that pertains to function execution I prefer macros within the body rather than outside.
https://github.com/donut-party/hooked is a recent take from a good source. I haven't gotten my hands to it yet, though
It's just a home-cooked multimethod with a docstring and a schema, fewer than 40 loc.
Alternatively, maybe one can use (ab)use Component/Integrant components, or Pedestal-like interceptors to add extra functionality.
I'm thinking of Pedestal interceptors most of all (they might be available in other modern frameworks)
The idea is that instead of using f
as a vanilla function, you inject f
into your system / interceptor chain, whatever, then you could transform it at will.
Pros: just data
Cons: most likely you'd lose some ide features
How can I set jvm properties with clojure cli. I run this with java java -DFoo=Bar -jar my.jar
. How do I have to inoke clojure? I tried this in several alternatives without success:
clojure -M -m namespace.to.main -J-DFoo=Bar
What am I missing here?
You have to put the property arguments first.
clojure -J-DFoo=Bar -M -m namespace.to.main
I just noticed that defonce
does not take a docstring as does def
. Is there any particular reason for that, or just a mistaken omission?
It’s surprisingly complicated, and there is a ticket for it :)
Wow, OK. I’ll go look for it. Inquiring minds want to know and all that. I figured that I couldn’t be the first person to notice. 🙂
@U7BEY9U10 You can use metadata to add a docstring:
user=> (defonce ^{:doc "I'm a docstring!"} foo :bar)
#'user/foo
user=> (doc foo)
-------------------------
user/foo
I'm a docstring!
nil
@U04V70XH6, thanks. I figured that was also possible and was my next step.
What is the easiest way to start a webview with Clojure on desktop?
It really depends on what you mean by "start a webview". You can look at how portal launches a browser: • https://github.com/djblue/portal/blob/master/src/portal/runtime/browser.cljc • https://github.com/djblue/portal/blob/master/src/portal/runtime/jvm/launcher.clj There are also a few options that are typically used for testing like: • selenium • https://github.com/clj-commons/etaoin • playwright.js (accessible via cljs)
Not for testing. For running an app. I'm exploring options outside of the standard Electron/Tauri. I would prefer to stay completely JVM.
Would the full app be running in the browser or would the browser just be a subcomponent?
Full app
I'm interested in sub component but right now full app is all I require.
Looks like Portal has figured it out
It's also worth noting that there's not really a difference between driving a web browser for testing or driving a web browser for other reasons.
Ah.. that is a good point
Portal is using Electron?
But yea, I would probably start with looking at what portal is doing.
I think portal has multiple options, one of them being electron. I think the default option is just to launch chrome and have it open a URL pointing to a locally hosted app.
I think for user testing I probably just want to do this. But I still want to figure out a nicer webview launching from java at some point 🙂
If you're willing to put in the work, you can use something like the Chromium Embedded Framework. It's quite a beast, but it's basically what every commercial product that embeds a web browser uses. I wrote some bindings a while back, but the API is very low level at the moment, https://github.com/phronmophobic/clj-cef.
It's quite capable though. Here's an example:
I did see this but got a bit overwhelmed by the code. But alright... this does seem like the best option.
I was already looking at learning some C and JNA for a different webview situation but realised it was a yak shave
Yea, it is a bit intense, but I'm unaware of any alternative.
I've had my eyes on this for a while. Even though it is only partially open. https://ultralig.ht/
I know cljfx has a webview component, but I think it fails to run many modern websites.
It's also not a full web browser. It's a complete strip down to the basics and built up from the bottom focused on performance.
the cljfx one is fairly broken from my experience
That ultra light one is actually the one I was trying to figure out JNA for today.
If you do decide to check out clj-cef or another cef wrapper, I'd be happy to offer advice, but it is quite a slog.
I just want a webview that has it's own context. Why is this so hard 😅
Only other alternatives I found in this space is. https://github.com/tauri-apps/wry https://github.com/webview/webview
Or at least work in this space
what about a JavaFX webview? I see cljfx mentioned on the thread but not sure if the limitations you are talking about are referring to cljfx or JavaFX webview itself
It’s not that good from my experience
but out of curiosity, why? just in case I have to use it in the future 😛
You can try it with cljfx there is a webview demo. It’s quite a janky experience. Using cljfx/JavaFX just for a webview that I’m not entirely happy with isn’t worth it and I would prefer a better alternative
I just tried with a plain javafx web view and did some googling and opening random pages, even youtube, it didn't look bad, but I probably see what you say, that it felt a little slower, not sure what is that about, shouldn't it just be webkit?
It is apparently WebKit and not far behind in updates. I’m not sure what is happening and what others experience of it is. I’ve also considered CEF but tbh it would be nice to have something simple, small, closer to Clojure and composable.
If you AOT (def x (merge {..} {..}))
, does the generated byte code store merged or unmerged value?
the compiler doesn't know that merge doesn't do side effects, doesn't know that you have redefined what clojure.core/merge is
But no, the compiler always generates code that runs to produce the effects and values, aot compilation just saves that same code off to the side on disk
if you disassemble the byte code
(let [x$ (+ 1 2)] (defn foo [] x$)
(A) you'd only see the value 3
in the disassembly
(let [x$ (+ 1 2)] (def foo x$)
(B) what about here ?
(def x (+ 1 2))
(C) what about here ?I am not at my computer but I am pretty sure you are dead wrong about what you would see in the bytecode for A
(the Clojure compiler may evaluate simple constant expressions during compilation?)
@U04V70XH6 hah yah, tricky compiler, pretend they're strings being concatenated
hiredman is correct about the compiler generating the code for the effects, not their results
The nature of the first depends on what context the expression is compiled in, but it will have the code for the let
@U0J3J79FE top-level expressions are compiled and they are evaluated each time the ns (or .class file) is loaded.
> cat src/jason/example.clj
(ns jason.example
(:gen-class))
(let [x$ (do (println "x$") (merge {:a 1} {:b 2}))]
(def x x$))
x$
will be printed when tests run, when the ns is compiled...
> clojure -T:build ci
Running tests in #{"test"}
x$
Testing jason.example-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
Copying source...
Compiling jason.example...
x$
Building JAR...
and then again when the AOT-compiled JAR is executed:
> java -jar target/net.clojars.jason/example-0.1.0-SNAPSHOT.jar
x$
there's no world where the class file that corresponds with function 'foo' has anything but 3 inside the byte code
So one of these two things must be true, so the class associated with function 'foo' isn't generated at AOT time, or it is but never executed, because then its regenerated when you java -jar
The compilation of the let emits code that loads the two numbers on the stack the adds them, then stores them in a method local that corresponds to the let binding, then loads the value from that method local and passes it to the constructor of the fns class
@U0J3J79FE Here's what javap
shows for (part of) your code after AOT compilation:
0: lconst_1
1: ldc2_w #12 // long 2l
4: invokestatic #19 // Method clojure/lang/Numbers.add:(JJ)J
So there's the (+ 1 2)
Clojure expression.
Right, I wanted to confirm what was actually in the bytecode after compilation of a minimal example.
That relegates 'sexp compile time optimization' strictly to defmacro usage, which is a sound model
The main thing folks "miss" when trying to form a mental model is that top-level expressions -- even when compiled -- still all run every time that ns is loaded. Which means they run during compilation (which has to load the ns to compile it) and it happens when the (compiled) program is run too.