Fork me on GitHub

with clj / deps.edn is the only way to add default jvm opts to use a profile explicitly?


(or of course supply -J... every time I start the process...)


@noisesmith I believe that is correct. I think there is a JIRA ticket to consider allowing :jvm-opts at the top-level.


OK, well that's very inconvenient


@seancorfield so there's no implicit base alias, or repl alias that gets merged onto?


You can kind of fake a default implicit alias by using a shell alias


yeah, I'm setting up project tooling for a new app, I dont' want to force my team to all use the same shell aliases or custom config


what I want is to make this lib work by providing the right system properties at vm startup, for now I guess my best option is to make everyone switch to a custom shell script instead of using clj/clojure directly


but that's still ugly


Is there a reason you're using JVM properties rather than some other approach?


Those seem pretty volatile to me since anyone can override them at startup, in their own environment.


it's the only way the config library accepts setting its config source


And it doesn't have a default source?


nope, weirdly enough


I guess I could set outpace.config.bootstrap/explicit-config-source but that still needs to be done in a way that all tasks end up invoking the code


oh - I see, the default is in current directory, not classpath


so it does have a default I can use


If your library code has control before outpace runs, you can set a default value for the JVM property if none is present.


(System/setProperty ,,,)


right, but I'd have to do this at every entrypoint


looks like just moving the file to the expected place would solve it (but then I need some other incompatible setup for running from a jar...)


(this sort of thing is why we wrote our own config library 🙂 )


At some point we will refactor it out of our code and open source it... but I've been saying that for a couple of years already so...


do you guys use cheshire for strucutred logging, as in logging JSON to the stdout to capturing it using another tool to send it to a logs aggregator?


I'd use something more dedicated to logging - eg. cambium


also, has a cleaner deps tree than cheshire


thanks, i will look into this cambium, i'm currently trying to combine timbre and cheshire


pretty big speed hit for clj.json


(i.e. cheshire is faster)


if your logging is a perf bottleneck you have bigger problems


but yes, cheshire is marginally faster


it also brings in a bunch of apache / jackson nonsense


jackson is the biggest source of painful / breaking dep tree problems in my entire software career

☝️ 4

but, imo, for well-defined, not-likely-to-change things, there’s a fair argument for “go with the faster thing”


@noisesmith there a much worse offenders


in the clojure space alone


(not to mention some java-centric things)


java logging alone is sig worse


there's one problem i'm facing with both org.clojure/data.json and cheshire, maybe you guys could help, i'm kinda new to clojure. timbre has an option that allows you to provide a function that will be responsible for generating the output based on the data sent to it to log, i provided a function like this one:

; for cheshire
(s/defn timbre-output [data]
  (-> data
      (json/generate-string data)
; for clojure
(s/defn timbre-output [data]
  (-> data


I dunno nuthin bout no timbre :derp:


println returns nil


I usually use tools.logging + logback


hmmm, this combination lets you send whatever object comes to you to your logging function and it manages to serialize it without exceptions?


@zignd your -> macro is providing two args to cheshire, data twice


I forget what its generate-string does with a second arg, but that's clearly not what you want


also, if that function is returning the thing you want to log, take out the println call


woah, that is true! @noisesmith thanks for mentioning!


if it is meant to do the output, println is a weird choice but I guess it could work


you can use doto if printing for debugging purposes (-> data json/write-str (doto println))


oh, i didn't know about doto :thinking_face: gonna check it out, thanks for mentioning!


the problem i'm facing is that both clojure/ and cheshire throws exceptions when they encounter an object they can't seem to convert to json, currently an instance of clojure.lang.Delay, i'd like to knows if there's any option to call .toString for objects in this condition, but couldn't find anything on the docs. although there's an option in both libraries that allows you to check the object's type and manually serialized them


each one accepts a global config offering rules to output custom types


the one with is nicer to work with IMHO


If all you want is to call .toString, then maybe a wrapper ns?


oh, i didn't know about doto :thinking_face: gonna check it out, thanks for mentioning!


e.g. a ns with (defn info [obj] (log/info (str obj)))


with you extend the JSONWriter protocol for each type - it has one method -write


so (extend-type clojure.lang.Delay JSONWriter (-write [this out] ...))


nice! now i do have a few things to look into, thank you @noisesmith and @potetm. cambium seems to be a better option in order to not reinvent the wheel, at least from what i could quickly read on their web site, in case it don't i will try the approach maybe checking for the protocol implementation with satisfies? and calling .toString when it doesn't :thinking_face:


Is there a good reason why println isn't concatenating on single-arity before it writes the newline to out ?


The current behavior means that newline could be out of order in a multi-thread scenario


yeah, println simply isn't thread safe, I wouldn't be surprised if that was intentional


like - if you need thread safe output, you need logging not println


Once in a while, I get burned trying to debug a concurrent algorithm at the repl, and realize the println isn't thread safe


Generally my fix has been to concatenate before I call println, but I realized today even the newline is not thread safe


So now I need to use (print (println-str a b c))


Or (print (str a " " b " " c \newline))


I wonder why println just doesn't do that in its implementation though


My only small guess is maybe it is assumed that .write on a stream is itself not thread safe, so they figured no guarantees exists and so didn't bother


But System.out appears to have a threadsafe write in OpenJDK at least


And I guess why it wouldn't also do the same for the multi-arity variant


Hello! Quick q: why are some values in clojure allowed to have the same hash? (e.g. 1 and true). Woudn’t that break hashmaps, etc?


What do you mean with same hash? The hash-function?


@US2BY67C0 yes, the function hash


@US2BY67C0 ah sorry, they have the same hash in ClojureScript but not in Clojure

💡 4

cljs.user=> (hash 1)
cljs.user=> (hash true)


@UMBC73RK9 uh; can you check my post in #clojurescript ? I got a different result with an online repl


maybe it’s using an old version of cljs


Okey I have no idea what's going on in the site. But my earlier paste is from my own cljs repl using latest clojurescript


What hash do you get for 1231 then on that version? (hash 1231)


cljs.user=> (hash 1231)
cljs.user=> (hash true)

😲 4

So I guess hash collisions are ok? Then my follow up question would be what does the docstring for hash mean by “consistent with equality”? Does it mean that if two values are equal then they have the same hash, but not necessarily that they’re equal if they have the same hash?


Yes, hash collisions are okay. “Consistent with equality,” means “Hash codes are the same when the objects are equal.” Hash code and equality are different things. The former is intended to be a cheap calculation to determine an object’s location in a data structure. The latter is intended to be a semantic equality check.


There are only 2 to the power 32 different 32-bit int values (about 4 billion), and that is the return type of hash in Clojure on Java. There are far more than that many 64-bit long values, so on average 2 to the power 32 different long values will have the same hash value as each other, and if you consider lists, vectors, maps, sets, etc. all implementing the hash function, too, they each have far more possible values than there are hash values.


This article mentions what hash being consistent with equality means, and describes at least a few cases where Clojure hash is not consistent with = (not many), and what = means in Clojure in detail:


Nevermind, this is in ClojureScript; I’ll post in the right channel 🙂


Anyone know of a good algorithm to group programmatically created string together? For example, "gke-qa-stable-06e4523d-grp" and "gke-qa-stable-06e4525d-grp" would belong together. "cachefiler01" and "l2cachefiler01" would not belong together. I've tried using levenshtein distance under some threshold to group strings together, but it gets several strings wrong. Ideally the algorithm would weight earlier letters more than later letters when computing the distance.

delaguardo16:12:55 have a look at this one. I found it more accurate for purpose of calculating weights of “autocomplete suggestions” that looks close to what you describe in example


Thanks, reading now!

Chris O’Donnell16:12:10

If the strings all have a regular structure, it seems like parsing them and grouping them based on their parsed attributes would be more reliable than trying to use a string similarity algorithm.


They don't have a regular structure 😞

😳 4

For address matching (street, city, …) we used quite successful the n-gram matching. (Similar to levenshtein). If you then add additional information about your strings, like grouping the sections separated by a ‘-’ you could get good predictions. Or have the first n-grams higher weighted than the last n-grams. In clojure it will be pretty easy to be implmented with the partition function.


Interesting. About 50% of my sample of names have a hyphen and the other 50% do not. For example "devices01" and "devices02".


This might give you an idea about an unweighted n-gram match. Good luck. But really interesting task you have. 😉


This won't really work because I have a giant list of strings that needs to be grouped. I'd need to know where in that sorted list to break apart into groups.


There may be something here though. Sort, partition into groups of 2, find groups where the levenshtein is less than some threshold or percentage of word size. May work well if the groups are often pretty dissimilar.


Requirements are pretty vague


Why are the strings being “grouped?”


Are you trying to derive some sort of partitioning scheme?


We have customers running VMs in the cloud. They have hundreds to thousands of instances running which are not grouped in any way besides name similarity. The goal is to help them understand what they are running by grouping things together.




so, my reframing might be: “Please parse these strings for metadata about the VM”


Sounds right


Which, if that’s accurate, is much easier (or only, really possible?) if you have a proper grammar


idk - at this point I’m pushing the convo way outside what you were asking


Definitely. It’s hard to enforce that at large enterprises though. These VMs cross several teams. They are interested in fixing what they have now and then work on the cultural problem.


Yeah I wonder how far a “simple” sort+filter might get you


At any rate, curious to see what you come up with. What you said about sort + levenshtein might just work gud enuf.

Joe Lane17:12:54

@kenny Look up Chris Oakman's talk on probabilistic record linkage on youtube from the last conj. It seems highly relevant to your situation except for domain differences. He even has source code in a repo on his github account.


Oh cool! I've done some work in the healthcare realm and know the data can be quite nasty. I'll be he's got some good ideas.


The sort + levenshtein only gets about 2% wrong. I’m going to watch that talk for fun but I’m guessing that error rate is acceptable.


what's the appropriate channel for questions regarding metabase-datomic?


is there a cider+company operation/function to list parameters of the function at the cursor?


something like this on cursive

Joe Lane17:12:07

The tool you're looking for is eldoc , which cider supports.


oh, it is even already enabled by default, it was displaying the doc at the bottom of the window and i didn't notice. sorry and thanks at the same time @U0CJ19XAM hahahah


FYI: is currently down. I'm working on fixing it now. The repo is readable, but search, uploads, and the API are unavailable.


Ok, it's back up. I was iterating on the ansible config and make a mistake there.

👍 12

Ok, it's back up. I was iterating on the ansible config and make a mistake there.

👍 12

Are there any thoughts about upgrading the Clojure license to EPL 2.0 from 1.0? I have been nudged to do that in one of my own Clojure projects to continue benefiting from the free open-source project tier at Netlify.


i saw something about netlify changing how they offer free tier. i think i remember they will require a code of conduct in the future as well?


Doesn't EPL 1.0 allow you to use 2.0 at the user discretion?

Alex Miller (Clojure team)21:12:01

So it’s unlikely Clojure itself will change


By the way, EPL 1.0/is an approved OSI license. So it seems it would fit the Netlify qualification


I didn't see it in the list


ah yes. it is included in the "All Approved Licenses"


@U0EHA00G1 according to Netlify's terms they should allow EPL 1.0 since its on


> Includes a license listed on the Open Source Initiative approved license list or a Creative Commons license that includes “attribution” or places the work in the public domain.


This is for the Open Source Plan which gives pro plan benefits for free. I did need to add my code of conduct to the project, and EPL 1.0 is not one of the available licenses because it’s deprecated to reduce license proliferation.


I don’t need Clojure to change, I was just curious about the thinking because of that license proliferation question. It’s probably inevitable because I’m sure Rich isn’t the only person to prefer 1.0.


The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version.


"Contributor" means any person or entity that distributes the Program.


Because of that, EPL 1.0 allows anyone to redistribute an EPL 1.0 program as an EPL 2.0 program.


Is there a strong drawback to using EPL2?


IANAL, but I'd say no. It all depends in your intent


EPL 2.0 clarifies that source distributed code as part of a program making use of an EPL 2.0 dependency can have its own code be distributed under a different license




Because EPL 1.0 doesn't have that, it isn't clear if say a JavaScript app which links against the EPL 1.0 code can itself be non EPL 1.0


...I was a bit worried because I upgraded epl1->2 mindlessly


Or to be honest, any Clojure code which doesn't do AOT


The part I'm not sure is why EPL 1.0 isn't suitable for scripting languages. I believe it is related to the wording using "module" instead of "file"


It makes it sound like by bundling your own files with EPL 1.0 files together in one module this would be considered a modified work of the original and therefore your new files should also be EPL 1.0


Which would be arguable for some scripting languages like JS


Since it doesn't have a clear concept of module


In Java, you could argue module means a given JAR, but even there it isn't super clear. Could it be the package? Or what is it in Clojure? Is it the namespace?


For example, if I take Clojure core and add 5 new functions to closure.core namespace? Is that derivative or modified?


In EPL 2.0 the language is very clear, if I modified an existing file, even if by only adding to it, it is modified and my additions fall under EPL as well

👍 4

Otherwise it isn't


In EPL 1.0 it is up to the courts.


In EPL 1.0, if I just add functions to the clojure.core namespace at runtime, even if I do this in my own file bundled in a seperate jar, someone could claim it is a modification of the closure.core module and thus needs to be licensed as EPL as well

👍 4

XML question... So I have one small piece of code that still uses clojure.xml with zippers. I was hoping that swapping out clojure.xml/parse for would be a drop-in replacement but now I'm getting NPEs from the code. Is there a guide to migrating from one library to the other, or at least some notable differences documented somewhere, so I can make the transition more easily? apparently I'm having a "bad coding day" and it is a drop-in replacement!


Ooh, did you keep a log of how you got rid of the other ones? I’d like to follow along and get rid of them myself, and that would be a very helpful starting point!


They were in third-party libraries so we mostly waited for JDK 11 compatibility to drop in those. It's taken a while!


Ah, I see. I am baffled about how to fix things like this:

WARNING: Illegal reflective access by clojure.lang.InjectedInvoker/0x0000000800231c40 (file:/Users/jim/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar) to method sun.awt.CGraphicsConfig.getBounds()
WARNING: Please consider reporting this to the maintainers of clojure.lang.InjectedInvoker/0x0000000800231c40


I just can’t imagine who to contact or how to tell them where to look.


@U0EHA00G1 It's from some library you are depending on that is using AWT.


(the invoker message obscures the origin of the warning)


A common source of the warning is using the legacy clojure.xml instead of the more modern (which is a separate dependency).


Yeah, that obscuration is what I was hoping for help undoing, is there a command-line flag or something to get back to at least the right Jar file if not line thereof? Sadly, I use like four score and seven libraries, if not more. But also sadly, I suspect this is probably seesaw, which has been abandoned for years.


Seesaw uses AWT so, yeah, that's most likely your culprit.


Seems like very basic Seesaw usage doesn't trigger it but I'm still pretty sure that's going to be your source...


I bet it’s the MigLayout library which is an embedded dependency of seesaw that I use for many of my layouts. That is a Java library that also has been abandoned for years, but I use it heavily. I suspect I will just be stuck on whatever version of the JDK still allows these reflective accesses until someday I rewrite my UI. There would be other benefits, since I would like to separate the engine from the UI so it can be used over the network with a variety of UIs, but that is going to be hundreds or thousands of hours of work, which is hard time to find for free-time free software. 😄


Although perhaps since it is coming from Clojure, that points more at seesaw itself. Anyway, thanks for your thoughts, I will put this back on the back burner for now.


@U0EHA00G1 Switching to JavaFX is probably your best long-term solution but I'll bet that will be a ton of work...


I used to think so too, but momentum has really flatlined for JavaFX recently and Oracle isn’t even shipping it any more, while they promise to support Swing through 2026. I’m starting to think that it’s going to be similar to the situation of JavaScript Single Page Application frameworks: every few years someone will come along and announce a new UI framework that will solve all the problems and avoid all the old mistakes, there will be a flurry of excitement, and then people will realize that the problems are fundamental to the task, and each new framework just rearranges them. If I do the work of decoupling my UI from the Beat Link Trigger engine (probably using GraphQL), I’m not sure what I would use to build the first new UI. I might just do a direct Swing one without seesaw, since I am a lot more comfortable with interop than I was when I started it—and I know Swing pretty well, as an author of the O’Reilly book on it. Or I might just do a web interface, and let other people build native GUIs on their platforms of choice. But none of this will happen soon.


Interesting. I've never needed to build UIs with Java so I haven't followed what happens with Swing and JavaFX. I sort of got the impression that Swing had been deprecated ages ago and JFX was the "hot new thing" but I've never used either...

seancorfield06:12:42 was JFX really meant to be so much better than Swing?


Sorry about the delay in responding, but I was overtaken by real-life events. 😉 My distant understanding is that there were a number of factors in play. First of all, desktop operating systems and hardware were changing, and things like high-DPI displays, where single pixels become hard to see, were causing Swing’s look and feels to become unmanageably tiny. And people like creating new things better than fixing existing ones, so JavaFX was launched to replace Swing, the way Swing replaced (although it still uses at bottom) AWT. But then another big change started sweeping the software world, and desktop operating themselves started to lose prominence for end-user interactive applications. And in the mobile (especially iOS) world, Java’s promise of “write once, run anywhere” became completely untenable. So a lot of the energy around building user experiences moved to web technologies, and more recently things like React Native. So even if JavaFX was magnificent, it could not be everything for everyone. Here is the nicest concise statement of where things stand today I have found:


Thanks for that link. Interesting. Makes me think of VHS and Betamax 🙂


Wow, JFX has been around for over 12 years... I didn't realize it was that long...


(I'm trying to eliminate all the Illegal Reflective Access warnings that JDK 11 highlights and this is the last one -- and I sort of expected it from clojure.xml)


FWIW, I've gotten rid of the NPEs (PEBKAC) and by specifying :include-node? #{:element} I have gotten much, much closer to the output of clojure.xml/parse but I'm still hunting down some differences that are causing all sorts of tests to fail... then I got it all working ahem


> PEBKAC had forgotten about that one :rolling_on_the_floor_laughing:


@seancorfield I've had a similar problem when upgrading from one version to another and (parse-str body :include-node? #{:element}) also was the solution for me


one could argue that whitespace preserving by default was a breaking change


It turned out to be something unrelated to somewhere else in the code being tested. The default for parse (with :include-node? #{:element :character} was what I needed after all. I was already trimming whitespace in this code (even tho', I guess, clojure.xml did not preserve that?).


What I mean is version 0.0.8:

user=> (xml/parse-str "  <foo>   <baz>hello</baz>  </foo>"){:tag :foo, :attrs {}, :content ({:tag :baz, :attrs {}, :content ("hello")})}
Version 0.2.0-alpha6:
user=> (xml/parse-str "  <foo>   <baz>hello</baz>  </foo>")
#xml/element{:tag :foo, :content ["   " #xml/element{:tag :baz, :content ["hello"]} "  "]}


So in version 0.2.0-alpha6, suddenly there are more elements in the content vector using the same API. This broke some of our code.


Yeah, I'm a bit surprised it isn't breaking our code. I saw that difference initially and I thought it was breaking our code, but it turned out I had something cached that was incompatible... and changing to #{:element} "worked" in terms of getting me past that problem -- but then I was missing all the text content I was expecting inside certain tags...


It says "Preserve whitespace by default" but I don't see an option for not preserving whitespace... Am I just missing something or...?



user=> (xml/parse-str "  <foo>   <baz>hello</baz>  </foo>" :skip-whitespace true)
#xml/element{:tag :foo, :content [#xml/element{:tag :baz, :content ["hello"]}]}


. This was what fixed our code, so ignore what I said about :include-node? before.


I can't find anything about this in the docs. Setting this to true by default should have been more compatible with previous versions probably.


Interesting. Thanks. I saw that option on pull-seq but not anywhere else.


I'll add that in and re-test our code.


Yup, that's all good. Thank you!