This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-10
Channels
- # announcements (48)
- # asami (8)
- # babashka (186)
- # beginners (56)
- # calva (42)
- # clerk (84)
- # clj-kondo (75)
- # cljdoc (21)
- # clojure (121)
- # clojure-art (1)
- # clojure-australia (1)
- # clojure-china (1)
- # clojure-conj (2)
- # clojure-europe (10)
- # clojure-filipino (1)
- # clojure-hk (1)
- # clojure-indonesia (1)
- # clojure-japan (1)
- # clojure-korea (1)
- # clojure-my (1)
- # clojure-nl (2)
- # clojure-norway (9)
- # clojure-sg (1)
- # clojure-taiwan (1)
- # clojure-uk (2)
- # clojurescript (11)
- # cursive (30)
- # datalevin (20)
- # datomic (4)
- # fulcro (5)
- # gratitude (1)
- # hyperfiddle (89)
- # introduce-yourself (1)
- # java (5)
- # jobs-discuss (8)
- # lsp (89)
- # malli (57)
- # membrane (16)
- # off-topic (12)
- # pathom (38)
- # releases (5)
- # shadow-cljs (17)
- # tools-deps (18)
- # xtdb (62)
Is there any .cljc
implementation of hiccup?
https://github.com/clj-commons/hickory/blob/master/src/cljc/hickory/render.cljc#L116
Depending on what it is you need to do, there is also daiquiri (a fork of sablono) in rum. It's not strictly cljc, but it's a tool for server and client-side rendering of hiccup data structures.
If I only read strings, do I need the "pushback buffer" capabilities of a PushbackReader? Or can I just move my cursor back and forth along the string?
Seems to me like the unread
capabilities are purely to handle read-only input streams, and that typically unread
is going to be called with whatever character was just read in
for simplicity, no. I'm slurping a file and working on the whole thing
If you are reading an entire file's contents into a string, then PushbackReader seems unlikely to be of any use to you.
It tends to be used and/or required in situations where some code is reading the file that is parsing it for some syntaxes where the parser uses "lookahead" techniques, e.g. Clojure's EDN reader and Lisp reader code, probably some JSON parsers, etc.
I noticed that async/timeout
channels are re-used if they are in a 10ms difference from each other. This code:
(require '[clojure.core.async :as async])
(def ^java.util.concurrent.Executor virtual-executor
(java.util.concurrent.Executors/newVirtualThreadPerTaskExecutor))
(def n 200000)
(let [chans (atom #{})
begin (System/currentTimeMillis)
state (atom 0)]
(dotimes [_ n]
(.execute virtual-executor
(fn []
(let [chan (async/timeout 1)]
(swap! chans conj chan)
(async/<!! chan))
(swap! state inc))))
(while (< @state n))
(println "Spawned" @state "threads and finished in" (- (System/currentTimeMillis) begin) "ms")
(prn (count @chans)))
for varying n
, in JDK19, launched with clj -J--enable-preview -M /tmp/timeout.clj
, will either show that @chans
is a very low number, much lower than n
, or it will error with:
java.lang.AssertionError: Assert failed: No more than 1024 pending takes are allowed on a single channel.
My question: why the buffering of timeout channels and could this be considered a bug in core.async, or intentional?
I found this conversation: https://groups.google.com/g/clojure/c/OSwKOKEupGc
One of the patches on https://clojure.atlassian.net/browse/ASYNC-234 adds an alternative timer implementation that gets you a unique readport every time you call timeout, and also stops timers from being able to hold references beyond when they are actually being waited for
For virtual threads I think it can be as simple as:
(defn timeout [ms]
(if virtual-executor
(let [chan (async/chan nil)]
(.submit virtual-executor (fn []
(Thread/sleep ms)
(async/close! chan)))
chan)
(async/timeout ms)))
the non-sharing wasn't the primary goal, the primary goal was to avoid leaking in active handlers, so without that change the new timers aren't usable as is
Is there an easy way to run exec
syscall? I don't see it listed in https://clojuredocs.org/clojure.java.shell If not, no problem, I can just use sh
, but exec
would be preferable.
As in "replace current process (JVM) with the new process". I want some preprocessing and then launching a command which should replace the JVM process, so then it automatically exits with exit status of the new process and I don't have to deal with possible exceptions etc.
... not to mention with clojure.java.shell/sh
I end up with :err
and :out
which is annoying and showing one and then the other might not represent how messages went in time (as there might be out then err then out).
i don't understand. sh will block until subprocess excited with some exit code. output and error then will have everything that subprocess send to stdout and stderr.
@U0NCTKEV8 OK that's fair enough, good to know.
Sucks but whatever 🙈
exec is a very posix thing, and the jvm is intended to be able to run in many different environments
Yeah I get that. I don't ever run out of POSIX though, so ... not a feature for me.
There is some support in GraalVM native image and babashka process does expose https://github.com/babashka/process/blob/master/API.md#babashka.process/exec. If you are using babashka, it is available.
Thanks @UE21H2HHD. It's a project I'm porting from Babashka to Clojure, so I can build it as one thing with everything (JAR in this case), so on the CI I don't have to install both Java and Babashka....and I already need Java/Clojure for ClojureScript, so the one who's out is Babashka, despite the fact that I really like it a lot.
In the future I want to look into native compilations with GraalVM, but for now Clojure/Java is the past of least resistance.
what's wrong with babashka + clojure combo? most of ci platforms can cache dependencies like jvm
I just don't want more dependencies, that's all.
OK for dev, not keen on too much things going on out of dev env.
right, because you mention ci i assumed this is to build something
It is.
It's a task manager, think of babashka's tasks in bb.edn
, but this takes it from literate programming files (org-mode). So instead of having tasks defined in one language like CLJ, you can just have:
#+name: install
#+begin_src sh
apt-get install ...
#+end_src
In your http://README.org or any .org file and read it directly with et install
.
That's the idea. And since it's a task runner, I want it to be as much standalone as possible. GraalVM binary would be best for the future.I have a project that's completely done as a literate programming and this allows me to run all the tasks that are defined/documented in the org files where the project lives.
:thumbsup:
Can I get some help writing a macro kinda like this, but working? It's a sort of chicken and egg issue.
(defmacro defm [name syms]
`(def ~name
(reify Module
(exec [vals#]
(for [i# (range 0 (count ~syms))
:let [~(get ~syms i#) (get vals# i#)]]
~@body)))))
(defm foo [a b] (+ a b))
(exec foo 1 2) ; => 3
hard to tell what the intent of the macro is, but should almost certainly just be a function
it is hard to tell from the above, but maybe this needs to be a macro for creating new bindings, but I am not sure
gist: a module encapsulates some computation and some data input signature; later we execute the module computation with some data that matches the signature;
in which case all you needs is to wrap a function in a module, which you can do with another function
the problem with people writing macros that are new def forms is people always conflate creating and X and binding X to a global top level name
and that sucks, imagine if the only way to create a function was defn, sure defn gets used a lot, but how annoying would it be if clojure provided defn and not also def and fn
like, macros don't have "temporal input" as a feature, so you would be using macros to building such a thing, so you must have some idea how those are represented in regular clojure code first
(the reason you must know that is because macros just expand into clojure code, so you have to know what your feature looks like in clojure code first)
and if you had the macro and it existed, what would the expansion for that example input be?
(defm sum [a] [b]
(0 (+ a b)))
(sum 1 (ch 2))
(reify Module
(exec [[1] (ch 2)]
(let [a 1
b (<!! ch)]
(+ a b))))
anyway, once you know what the input looks lie (what it looks like to call your macro) and what the output looks like (what the clojure code that actually runs looks like) you just need to write a function that is a mechanical translation between the first data structure and the second
;; A module has a spatial in and temporal in
;; and can execute
;; and can return a spatial out and temporal out in a pair vector
(defprotocol Module
(exec [sin tin]))
;; A module with 2 slot spatial input (a, b)
;; and 2 slot temporal input (c, d)
;; returns 0 in spatial output
;; and speaks the 2 sums in the temporal out
(defm sum [a b] [c d]
[[0] ;; spatial out
[(+ a c) (+ b d)]]) ;; temporal out
(def c (a/channel)) ;; a chanel for temporal input
(def d (a/channel)) ;; another chanel for temporal input
(exec my-sum [1 2] [c d])
(a/>!! c 3)
(a/>!! d 4)
;; defm above should expand to
(reify Module
(exec [[a b] [c d]]
(let [c' (<!! c)
d' (<!! d)]
[(+ a c') ;; spatial out
(+ b d')]))) ;; temporal out
(reify Module
(exec [[a b] [c' d']]
(let [c (<!! c')
d (<!! d')]
[(+ a c)
(+ b d)])))
(it matters where you put the new names because you don't want to traverse through the body and rewrite the names)
and that isn't actually pristine because you are missing the this
argument for the reify
a macro is a translation from A to B, it is easier to write the translation if you have B around, complete, and working already
https://gist.github.com/hiredman/1179073 might be a useful reference, it is doing some manipulation of bindings, in the bodies of functions some names are bound to delays instead of the actual values so those need to be forced and rebound
OK, follow-up question: some of the commands I have are interactive, like running a REPL etc. I can't use https://clojuredocs.org/clojure.java.shell for this. (Another issue that would be avoided if there would be the bloody syscall exec ...). How do I do that, running an interactive command from CLJ?
Why can't use use clojure.java.shell
or https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/ProcessBuilder.html?
clojure.java.shell/sh
exits immediately. For instance when running a Deno REPL:
(sh "deno" "repl")
{:exit 0, :out "Deno 1.31.1\nexit using ctrl+d, ctrl+c, or close()\n", :err ""}
Does ProcessBuilder work with interactive commands? Before I start digging into it, I know fuck all about Java.
you probably need to pass in a stdin
Well I don't know what I'm going to type to the REPL yet!
It's to run an interactive command.
this will be interactive @U024VBA4FD5
Oh nice! Thanks @U3JH98J4R
specifically what interactive command are you trying to run? there are ways with ProcessBuilder to make this mostly work with simple readline-based interactivity. I can dig up an example in a few. I have never been successful in getting this to work for applications that expect full TTY control (e.g., console Emacs).
@U060QM7AA deno repl
which is Deno (JS) REPL.
(require '[babashka.process :refer [shell]])
(shell "node")
Also checks the exit codeWhat is the purpose of IPersistentMap.assocEx
? I see that it first checks that the key is not present and asserts if it is. But I don't see it actually being used anywhere in the Clojure code; it's just being defined for the different map types.
It was used up to 2007 by PersistentHashtreeMap
, but then that class got removed. It's just guessing on my part, but the method was probably forgotten till later, when it was too late to remove it.
Or maybe something outside of org.clojure/clojure
was using it even back then.
A few references here: https://grep.app/search?q=.assocEx&filter[lang][0]=Java&filter[lang][1]=Clojure
Ohh, never seen http://grep.app before. Thanks.
Looks like something I can avoid for jank. Happy to find things I don't need to implement. 🙂
yeah, http://grep.app is very useful for research like this
another thing I use a lot for "who is doing what in clojure and how often" is this: https://github.com/borkdude/grasp
It’s vestigial