This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-18
Channels
- # babashka (12)
- # beginners (35)
- # biff (6)
- # calva (23)
- # cider (7)
- # clj-kondo (10)
- # cljs-dev (15)
- # clojure (81)
- # clojure-dev (2)
- # clojure-europe (13)
- # clojure-germany (1)
- # clojure-korea (2)
- # clojure-nl (1)
- # clojure-norway (19)
- # clojure-uk (7)
- # clojurescript (23)
- # core-typed (33)
- # cursive (7)
- # data-science (7)
- # datalevin (9)
- # hyperfiddle (1)
- # introduce-yourself (2)
- # malli (1)
- # matrix (17)
- # missionary (24)
- # music (1)
- # off-topic (15)
- # polylith (6)
- # reagent (10)
- # releases (5)
- # remote-jobs (1)
- # shadow-cljs (3)
- # squint (7)
- # xtdb (11)
- # yamlscript (6)
is there a suffix to put on clojure scratch files to indicate that they contain clojure code, but should not be loaded as part of the project? In scala I can use .scala
to mean a file that the compiler needs to consider when compiling the project, and .sc
which is a file the compiler should ignore unless explicitly loaded.
I think in clojure, a file won’t get loaded anyway unless explicitly required. so such a suffix is perhaps not technically needed. however, it might be nice to distinguishing them to a human pursuing the project.
The idiom I've seen for this is to put scratch files in a separate repl_sessions
directory.
yeah, I put them on a dev-src folder, and then have a :dev
alias in deps.edn with :extra-paths ["dev-src"]
How does
work? Is it just something that searches the files in the classpath? So anything that is in :paths
in deps.edn
is discoverable by
? If so, how are the files searched and what would happen if classpath had 2 files with the same name?
For the context, I am making a shared edn file that defines build ids and which are meant to be consumed by bb, shadow and backend. I thought that making that edn file as a resource is the way to go and I managed to obtain the file via resource
function although I am not sure how it works.
I think you’re on the right track, and that this is a good use case for io/resource. Collisions are often avoided with namespaces. If I’m making an app
eu.teod.weeknotes-notes
I’d put an image for that app in a path like
resources/eu/teod/weeknotes-notes/book.png
The Clojure documentation (https://clojure.github.io/clojure/clojure.java.io-api.html#clojure.java.io/resource) has a link to "Source" (https://github.com/clojure/clojure/blob/ee1b606ad066ac8df2efd4a6b8d0d365c206f5bf/src/clj/clojure/java/io.clj#L446) which shows that it's merely a call to Java's .getResource on the context class loader, https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#getResource-java.lang.String-. Bottom line, there might not be guarantees about how the case of multiple matching resources would be handled. Better to avoid that case!
Answering the original questions, yes it searches the classpath and can find anything in your deps. If multiple, it will find the first one on your classpath. Classpath order for deps.edn is defined here: https://clojure.org/reference/deps_edn#deps
Generally, just as with namespaces, you should place resources at a path you “control” via reverse dns, trademark, etc
Ah, now I get it, I should treat my resources the same way I do with Clojure source files via namespaces. Thank you for the detailed answers!
What's the best way to cancel some running futures? I'm having issues with below. Simplified code:
(def running-workers (atom []))
(defn cancel-all []
(doseq [worker @running-workers]
(future-cancel worker))
(reset! running-workers []))
(defn run []
(cancel-all)
(let [fs (for [i work]
(future
(let [vals (get-vals i)
_ (doseq [val vals]
(try
;... fetch from db and do some transforms and write to files
(catch Exception e
(println "Error: " e))))])))]
(swap! running-workers into fs)
(doall fs)))
(comment
(run)
(cancel-all)
)
cancel-all
doesn't properly cancel all threads. I'm guessing this is because the cancel interrupt is caught by the catch
?
Is there a better way/lib to do this?Cancelling futures isn't all that straightforward. There are lots of elements to stopping a thread from running. future-cancel
calls this: https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/concurrent/Future.html#cancel(boolean) which may end up calling https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/lang/Thread.html#interrupt() and depending on what's happening at the time that is called, it might be your responsibility to check the interrupted status on the thread because it's not guaranteed to throw an InturruptedException
, for example. If you want total control over how the process stops, it may be best to implement some kind of mechanism yourself, like an atom
you can check in the loop, or other control latch.
Yeah what I always do is to do looping (loop, while, etc) and check with (while (not (.isInterrupted (Thread/currentThread)) ...)
for your case, you can do like :
(future
(let [vals (get-vals i)]
(doseq [val vals
:while (not (.isInterrupted (Thread/currentThread)))])
(try
;;... fetch from db and do some transforms and write to files
(catch Exception e
(println "Error: " e)))))
One issue with relying on the interrupted state on a thread is that, depending on what you're doing, you may want the ;... fetch from db and do some transforms and write to files
to complete all or none of it's work when being cancelled.
Used @U0739PUFQ suggestion. Is it wrong to think that this is a common use case?
it is, but there is no general solution. The Thread class used to have thread.stop() but it has been deprecated. So the only safe way of stopping threads on the JVM is that the code adds check points to see if it should continue. See here for more details https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/doc-files/threadPrimitiveDeprecation.html
Is there a way to convert any exception to something that conforms to IExceptionInfo
so that ex-data
can log it out nicely/consistently? I still can't figure out how to log exceptions in Clojure, I still occasionally get nil
in my logs because something failed deep in a Java dependency 😞
how are you logging exceptions? I’ve found that (log/error e "error doing foo")
can get you pretty far
I use mulog
, so it looks something like this:
(mu/log ::exception-during-task
:request-id rid
:message (.getMessage e)
:data (ex-data e)
)
(try
(something)
(catch Exception x
(μ/log ::actionX :exception x :status :failed)))
are you willing to just throw the whole exception in there like this example?user=> (μ/log (Exception. "bad thing happened"))
nil
user=> {:mulog/trace-id #mulog/flake "4wS_ubSjkxAliogCJddxJAkILQ7cURd2", :mulog/timestamp 1713450240939, :mulog/event-name #error { :cause "bad thing happened" :via [{:type java.lang.Exception :message "bad thing happened" :at [user$eval794 invokeStatic "NO_SOURCE_FILE" 1]}] :trace [[user$eval794 invokeStatic "NO_SOURCE_FILE" 1] [user$eval794 invoke "NO_SOURCE_FILE" 1] [clojure.lang.Compiler eval "Compiler.java" 7194] [clojure.lang.Compiler eval "Compiler.java" 7149] [clojure.core$eval invokeStatic "core.clj" 3216] [clojure.core$eval invoke "core.clj" 3212] [clojure.main$repl$read_eval_print__9206$fn__9209 invoke "main.clj" 437] [clojure.main$repl$read_eval_print__9206 invoke "main.clj" 437] [clojure.main$repl$fn__9215 invoke "main.clj" 458] [clojure.main$repl invokeStatic "main.clj" 458] [clojure.main$repl_opt invokeStatic "main.clj" 522] [clojure.main$main invokeStatic "main.clj" 667] [clojure.main$main doInvoke "main.clj" 616] [clojure.lang.RestFn invoke "RestFn.java" 397] [clojure.lang.AFn applyToHelper "AFn.java" 152] [clojure.lang.RestFn applyTo "RestFn.java" 132] [clojure.lang.Var applyTo "Var.java" 705] [clojure.main main "main.java" 40]]}, :mulog/namespace "user"}
ex-info
accepts at third arg that can be any Exception as a cause. In Java land this is for chaining exceptions together, does this log any differently?
if the exception isn't an ExInfo, then ex-data will be nil anyway, right? what is the desired outcome here?
Ed, I think that helps a lot - you're suggesting that I basically use ex-info on exceptions that don't conform to force them to conform? That's exactly what I was looking for. Noah - ideally looking to make every exception an ExInfo so it can log nicely
Any exception can have a cause - it's an optional field on the Exception class. Using ex-info is an easy way to wrap exceptions so you can attach clojure data. But you're never going to be able to do that for all cases. You can get a stack overflow or an out of memory error at any point, for example. So it kinda depends on what your goal is. If you want to tag exceptions at some point in your program and log them in some generic way (like in middleware or something) then that'll help you tag them with useful data. But if you just want to wrap exceptions inside the logging function, you're not going to have any meaningful data to attach, like Noah says, right? So maybe you just want something like
(mu/log ::exception-during-task
:request-id rid
:message (.getMessage e)
:data (if (instance? IExceptionInfo e) (ex-data e) :no-ex-data))
if you don't want to log nil
?For ratio e.g. (/ 8 3) will return 8/3 1. What is the use case of ratio? Like when should I really use it? I googled it, and it says ratio can give accuracy compare to float. But still when should I even use ratio at the first place? 2. What if I am calculating a long formula and expecting an integer or float, but somehow it returns a ratio? Won't this cause my program crash or something? Assume some variables to the formula will either result in a integer/floating number and sometimes a ratio?
if you are doing integer math then division will create ratios, and be exact. if you want decimals, use decimals. (/ 8.0 3.0)
Clojure ratios are Java Numbers, and can be coerced to any other numeric type if needed. Nothing will crash.
(def a
(-> (/ 8 3)
(* 5)
(+ 1)
(* 7)
(/ 9)))
;; => 301/27
(double a)
;; => 11.14814814814815
(def b
(-> (/ 8.0 3.0)
(* 5.0)
(+ 1.0)
(* 7.0)
(/ 9.0)))
;; => 11.148148148148147
I see, so the decimal one is more accurate as it has a scale of 15. So I assume when I divide, best I use decimal. So the ratio seems redundant and not recommended to use?