This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-04-15
Channels
- # announcements (1)
- # babashka (21)
- # beginners (23)
- # biff (8)
- # boot (1)
- # cider (4)
- # clerk (21)
- # clj-kondo (31)
- # clojure (60)
- # clojure-brasil (5)
- # clojure-europe (20)
- # clojurescript (21)
- # datomic (14)
- # graalvm (10)
- # honeysql (1)
- # hoplon (4)
- # hyperfiddle (42)
- # introduce-yourself (1)
- # leiningen (1)
- # lsp (53)
- # pathom (4)
- # releases (2)
- # scittle (24)
- # shadow-cljs (3)
- # xtdb (4)
Yes, the Clojure/conj is in a couple weeks Apr 27-28 in Durham NC and has both online and streaming tickets https://2023.clojure-conj.org
Hey there! Iām wondering if keywords are good candidates to be used as lock objects with the locking
macro. I noticed that keywords seem to be interned based on this REPL test:
(identical? (keyword "a" "b") (keyword "a" "b"))
=> true
Iām implementing a simple lock-by-key mechanism. I understand the lock object should be unique and immutable, and keywords seem to fit that criteria. But Iām not sure if there are any caveats or considerations that I should be aware of.Why not just create an (Object.)
and lock that one, instead of relying on keywords being interned? keywords are kept via a weak reference so they may be garbage collected at one point if I remember correctly
I see what you mean about using an (Object.)
instead of a keyword, but I would need to create a map of lock objects by key and ensure that the map is initialized in a thread-safe way to solve my problem.
In any case, based on what you mentioned about weak reference and garbage collection, keywords are not an option for a lock-by-key mechanism after all. Thanks for your help!
I don't think weak references matters there, because if there are any maps containing those keywords (which I think is the case here), then there are going to be hard references to them and they will not be collected. IMO is ok to use it there if you are not using keywords as locks also in other part of the system
You should not use keywords as lock objects (a similar analogy is interned strings in Java, you can find similar advice there).
is there any reason for that apart from the problem if you use it on different part of the system and then ending up using the same lock in two unrelated places?
A ConcurrentHashMap of keyword to lock object is a fine solution here
Yes, thatās why this is considered problematic. It may also interfere with the keyword gc, but really thatās āit dependsā
out of curiosity, how can it interfere with keyword gc? if you are locking on them is because there is already a hard reference to them anyway
Are you removing them when youāre done?
I will also say that the identity of keywords could possibly be considered an implementation detail, not something you should necessarily rely on
Iām not sure if thatās ever something we promise anywhere
Kinda, in the FAQ: > This means that a keyword is reused (reducing memory) everywhere in your program and that checks for equality really become checks for identity (which are fast).
Thanks for your input, Alex. Based on your suggestion, Iāll use ConcurrentHashMap
to store the lock objects by key. While my initial test using keywords as lock objects worked on a small scale, thereās no guarantee that itāll work in a real-world application.
Since ClojureScript does not support identical?
for keywords, but instead provides https://cljs.github.io/api/cljs.core/keyword-identicalQMARK, it seems that keywords are somewhat host-flavored. I wonder why Clojure does not provide keyword-identical?
.
I wonder if its possible to get add-lib , add-libs and sync-deps to work with https://github.com/bhauman/rebel-readline/ , I guess rebel doesnt trigger what ever makes these libraries available šæ Ah, I need to require the clojure.repl.deps namespace....
user=> (require '[clojure.repl.deps :refer :all])
nil
user=> (add-lib 'hiccup)
Downloading: org/clojure/tools.deps.cli/0.9.35/tools.deps.cli-0.9.35.pom from central
Downloading: org/clojure/tools.deps/0.18.1327/tools.deps-0.18.1327.pom from central
Downloading: org/clojure/tools.deps/0.18.1327/tools.deps-0.18.1327.jar from central
Downloading: org/clojure/tools.deps.cli/0.9.35/tools.deps.cli-0.9.35.jar from central
Downloading: hiccup/hiccup/maven-metadata.xml from clojars
[hiccup]
user=>
@U05254DQM what doesn't it trigger? do you mean the clojure.main/*repl*
?
you could set that manually
@U05254DQM It does work for me:
$ clojure -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.12.0-alpha2"}}}' -M:rebel
[Rebel readline] Type :repl/help for online help info
user=> *clojure-version*
{:major 1, :minor 12, :incremental 0, :qualifier "alpha2"}
user=> (require '[clojure.repl.deps :as deps
#_=> ])
nil
user=> (deps/add-lib 'org.clojure/core.cache)
Downloading: org/clojure/core.cache/maven-metadata.xml from central
[org.clojure/core.cache org.clojure/data.priority-map]
user=> (require '[clojure.core.cache])
nil
The alias I have:
:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "rebel-readline.main"]}
Yes it would be nice to set clojure.main/*repl*
to true some how, so the require would happen automagically...
The alias I use also includes nREPL server, so is a little more in it, but I think rebel has its own tricks to start the repl which arent triggering add-libs etc.
:repl/rebel
{:extra-deps {nrepl/nrepl {:mvn/version "1.0.0"}
cider/cider-nrepl {:mvn/version "0.30.0"}
com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "nrepl.cmdline"
"--middleware" "[cider.nrepl/cider-middleware]"
"--interactive"
"-f" "rebel-readline.main/-main"]}
A require is an okay approach. It means the functions are only loaded when I decide to use them, so I just need to remember to require clojure.repl.deps. I'll take a look at the rebel readline code and see if there is an alternative approach. Thanks.
Pretty such clojure.core/*repl*
is true
in this scenario because clojure.main
sets it fo any -M
invocation at the moment.
What's missing is the auto-refer, probably because Rebel refers a specific list of symbols?
Yeah, confirmed:
(~/clojure)-(!2011)-> clojure -M:1.12:rebel
[Rebel readline] Type :repl/help for online help info
user=> clojure.core/*repl*
true
user=> add-lib
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: add-lib in this context
user=>
rebel-readline should probably be using clojure.main/repl-requires which is the list namespaces + vars that gets loaded + referred in your repl
https://github.com/clojure/clojure/blob/master/src/clj/clojure/main.clj#L355-L360 in Clojure 1.12 Alpha 2. Haven't found yet where Rebel refers in stuff...
Ah, Rebel doesn't refer anything in by default... doc
, apropos
, etc -- none of those are available directly, but Rebel provides hot keys to invoke the equivalent functionality.
I guess you could write an alias that first does "-e" "(apply require clojure.main/repl-requires)" and then starts the rebel REPL
yes, this works:
:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-e" "(apply require clojure.main/repl-requires)"
"-m" "rebel-readline.main"]}
@U05254DQM Someone raised the lack of source
years ago in Rebel Readline but when the ^X^S
hotkey was pointed out, they closed the ticket: https://github.com/bhauman/rebel-readline/issues/136
(and since Rebel also supports cljs, I suspect such auto-refer would have to be clj only?)
ok, I. made a comment there: https://github.com/bhauman/rebel-readline/issues/136#issuecomment-1509920017
Regarding add-lib, why invoke the deps tool as an external process and not from within the same jvm?
Emphasis mine: > Library resolution and download are provided by https://github.com/clojure/tools.deps. However, you do not want to add tools.deps and its many dependencies to your project classpath during development, and thus we have also added a new api for invoking functions out of process via the Clojure CLI.
Okay, sure, I don't want that BUT If I'm going to dynamically load stuff, why not make that the first thing I dynamically load?
I assume that adding tools.deps dynamically can still potentially interfere with already loaded stuff and maybe with things that will be loaded later. In ways that you do not want. And if you can already use it via CLI, why add yet another mechanism of using it that brings no substantial benefit but has a cost?
You're already on a virtual machine, why fork out to the underlying operating system?
IMO that would be trading aesthetics for practicality.
Having tools.deps
on a classpath during development is not fun. Been there, done that. Quite glad to have it in an independent, isolated process.
An isolating class loader was what Boot promised in "pods" (and failed to deliver) and it's what Polylith's built-in test runner attempts to do (and also fails). Part of why we abandoned Boot at work was because its "isolation" was buggy in annoying and often somewhat unpredictable ways -- and it's why I wrote an external test runner for Polylith, because we couldn't use the built-in one due to failures in isolation (again, buggy, annoying, and unpredictable).
OSGi offers this but it's a nightmare to work with in the general case (and also why we abandoned another tech at work, because it decided to switch to OSGi for module loading to provide "isolation" -- but that broke our Clojure embedding workflow in nasty ways).
invoke-tool is ^:dynamic, so itās amenable to swapping
Keeping clojure itself lean is what hoped for and therefore I love the current solution. No maven/aether/s3 etc dependencies as part of clojure forced upon users, no issues with GraalVM native-image accidentally pulling in all kinds of AWS dependencies into binaries, etc, while still having the dynamic deps feature. Well done.
Similarly, shelling out to git instead of using a Java lib that implements git turned out to be the better choice for tools.deps itself.
So... invoke-tool is amenable to swapping huh š https://github.com/borkdude/lein-repl-deps
"want" is a big word. I want lots of things. But if I take time to tackle anything at that level it will be building something like Unison's code manager for Clojure
i've been having a lot of trouble trying to pretty print some xml, i'm using clojure.data.xml
(->>
[:div {:class "my-class"}
[:h1 "Hello World"]
[:p "This is a paragraph."]]
hiccup.core/html
clojure.data.xml/indent-str
)
i get Reference is not allowed in prolog.
and i assume it's because of some missing html tags, but i try with hiccup.page/html5 and i get the same error. i don't want to pretty print html, just xml. i've searched for solutions and poked at clojure.data.xml, and i'm just stuck.ok, i was previously searching for solutions to pretty print html, but when i searched for just xml i was able to find something that worked
(defn pp-xml [xml-str]
(let [xml-data (clojure.xml/parse
(java.io.ByteArrayInputStream.
(.getBytes xml-str)))
pretty-xml (xml/indent-str xml-data)]
(println pretty-xml)))
guess this is solved, but maybe there are better solutions that people know aboutDepends on what you need. Thereās clojure.data.xml/parse-str
thatāll let you get away with less code:
(-> "<a/>" xml/parse-str xml/indent-str)
;;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<a/>\n"
If you care about performance, indent-str
probably isnāt the best choice, though.