Fork me on GitHub
#clojure
<
2023-04-15
>
E SKY BD13:04:23

are there going to any clojure conferences to be held on next month

Alex Miller (Clojure team)13:04:44

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

šŸŽ‰ 2
Henrique Prange14:04:38

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.

borkdude14:04:56

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

Henrique Prange14:04:34

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!

jpmonettas14:04:53

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

Alex Miller (Clojure team)14:04:44

You should not use keywords as lock objects (a similar analogy is interned strings in Java, you can find similar advice there).

šŸ‘ 4
jpmonettas14:04:36

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?

Alex Miller (Clojure team)14:04:42

A ConcurrentHashMap of keyword to lock object is a fine solution here

Alex Miller (Clojure team)14:04:59

Yes, thatā€™s why this is considered problematic. It may also interfere with the keyword gc, but really thatā€™s ā€œit dependsā€

jpmonettas15:04:08

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

Alex Miller (Clojure team)15:04:26

Are you removing them when youā€™re done?

Alex Miller (Clojure team)15:04:48

I will also say that the identity of keywords could possibly be considered an implementation detail, not something you should necessarily rely on

šŸ‘† 2
šŸ‘ 4
Alex Miller (Clojure team)15:04:39

Iā€™m not sure if thatā€™s ever something we promise anywhere

p-himik15:04:27

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).

Henrique Prange15:04:03

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.

phill16:04:05

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?.

practicalli-johnny15:04:34

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=> 

borkdude15:04:41

@U05254DQM what doesn't it trigger? do you mean the clojure.main/*repl* ? you could set that manually

borkdude15:04:53

@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

borkdude15:04:18

The alias I have:

:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
                   :main-opts  ["-m" "rebel-readline.main"]}

practicalli-johnny15:04:35

Yes it would be nice to set clojure.main/*repl* to true some how, so the require would happen automagically...

practicalli-johnny15:04:41

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"]}

borkdude15:04:17

Well, you can always do the require yourself I guess

borkdude15:04:45

which you already did in your original message, I missed that, sorry :)

practicalli-johnny15:04:49

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.

seancorfield17:04:58

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?

seancorfield17:04:10

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=>

borkdude18:04:03

rebel-readline should probably be using clojure.main/repl-requires which is the list namespaces + vars that gets loaded + referred in your repl

seancorfield18:04:49

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...

seancorfield18:04:46

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.

borkdude18:04:48

interesting

borkdude18:04:51

I guess you could write an alias that first does "-e" "(apply require clojure.main/repl-requires)" and then starts the rebel REPL

borkdude18:04:48

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"]}

2
šŸ‘ 2
seancorfield18:04:02

@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

seancorfield18:04:39

(and since Rebel also supports cljs, I suspect such auto-refer would have to be clj only?)

practicalli-johnny18:04:42

The -e alias approach is very nice, thanks everyone.

šŸ‘ 2
Ben Sless15:04:44

Regarding add-lib, why invoke the deps tool as an external process and not from within the same jvm?

p-himik15:04:55

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.

šŸ‘Œ 4
Ben Sless16:04:47

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?

p-himik16:04:24

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?

Ben Sless16:04:50

Because the idea of a single independent process appeals to me

Ben Sless16:04:53

You're already on a virtual machine, why fork out to the underlying operating system?

p-himik17:04:28

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.

Ben Sless17:04:57

We have to go deeper - we need an isolating class loader

seancorfield18:04:56

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).

šŸ‘€ 2
seancorfield18:04:14

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).

Alex Miller (Clojure team)18:04:40

If you want it, build it :)

2
šŸ‘ 2
Alex Miller (Clojure team)18:04:43

invoke-tool is ^:dynamic, so itā€™s amenable to swapping

borkdude18:04:21

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.

borkdude18:04:30

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.

borkdude19:04:49

So... invoke-tool is amenable to swapping huh šŸ™ˆ https://github.com/borkdude/lein-repl-deps

Ben Sless20:04:42

"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

šŸ™ 2
Ben Sless20:04:35

I just want to get one over the CLers

pppaul21:04:40

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.

pppaul21:04:28

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 about

flowthing10:04:44

Depends 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.

pppaul00:04:37

i don't care about performance, this template stuff is done once per app launch. i'll try your solution