Fork me on GitHub

$ lein do clean, classpath


Aha [lein-classpath-jar "0.1.0"] - removing this "fixes" it


When generating a CSV as a web handler response, is using and writing CSV to a writer the most recommended approach?


depends, the easiest thing, data size permitting, just write it out as a blob in memory and return that


I should have known I was over-complicating things lol.


how do folks deal with AssertionErrors thrown by :pre and :post conditions? We’ve analyzed the libraries we depend on and see that it’s not uncommon to use :pre and :post conditions for argument checking (something that should really throw IllegalArguementException and not AssertionError. 1. turn off assertions in production 2. catch Throwable or Exception + AssertionError 3. let our whole webapp terminate & restart whenever one request fails a :pre or :post condition All of the above seem not ideal. Also wondering if there’s been a prior discussion of the decision to let pre & post conditions throw AssertionError s.

Alex Miller (Clojure team)14:03:01

assertions are designed to be turned off in production

Alex Miller (Clojure team)14:03:40

and should be invariants that indicate a serious error - basically these should only fail if the program is wrong, not if a user provides invalid input


@alexmiller I’m aware of the latter, and I know that folks do turn them off but that also doesn’t seem ideal?


is this what you do and recommend? Turn off assertions in prod?

Alex Miller (Clojure team)14:03:12

if the program is right, they are redundant. testing is for checking that the program is right


The trouble is that you can rarely reproduce everything that happens in production in a testing environment. I found assertions (used sparingly) very useful and definitely valuable in production.

👍 1

we’re using to build the uberjar. Would this also be the place to turn them off?

Alex Miller (Clojure team)14:03:33

the place it matters is during compilation, so yes (but I'm not actually sure how easy that is to do!)

Alex Miller (Clojure team)14:03:48

might be a needed feature in


that might explain why our attempts to turn it off failed so far 😼


so maybe something around -disableassertions when invoking java?

Alex Miller (Clojure team)14:03:07

Clojure doesn't use Java assertions so that won't do anything - it's a dynamic variable that needs to be set assert


also wondering how much this simple right / wrong categorization applies to a multi-user application like a web service


on further thought, are those two statements really compatible: 1. assertions are designed to be turned off in production 2. and should be invariants that indicate a serious error if there’s a serious error in production, I think I’d like to know about it early, rather than continue.

Alex Miller (Clojure team)14:03:46

assertions are designed so that they can be turned off in production to remove the cost of the assertion. they are designed to catch logically impossible situations that can only occur if the program is wrong. they should not be used to detect invalid input (like a bad web request, bad user input, etc). you can use them in production, but the AssertionErrors are, by definition, not designed to be caught


but not catching them and not turning them off in production means that either one needs to make sure that every library one uses follows that exact meaning (which I’ve found to be not true for our deps) or live with the fact that our app will terminate on a bad request

Alex Miller (Clojure team)15:03:08

your complaint that people are not use pre/post conditions correctly is a valid one, but those seem like concerns to levy on the libraries in question


why does the app terminate?

Drew Verlee15:03:11

It's a matter of communication, catching an error in the running program then continuing (as opposed to halting) turns the error into a control flow mechanism like an If or when branching statement. Using it that way, means there is a input case to your system it knows it needs to handle, if that's true, is it really an "error" that needs to halt the program, which is the the natural progression of a throw? Thats how i see it at least. Not something that i have had to deal a lot with thankfully.


@ghadi I think was mixing up two things, it’s just the thread that will terminate, not the whole app, correct?


not necessarily


a supervising try/catch block can translate an AssertionError into a e.g. status 500


even if they're not designed to be caught


I was also thinking of the case where you have a single thread that does a unit of work that you want to make sure keeps running, is catching AssertionError valid in this case?


it all depends on the nature of the unit of work, and whether it's resumable


if it's an ongoing process vs. web request, etc.


catching Throwable will catch an AssertionError (among other things, like an OOM)


lots of facilities catch Throwable


what I'm saying is: it's not a guarantee that your app will terminate, unless something is trapping the AE and terminating


ok, so you say never catch Throwable isn’t something to follow in every situation


right. like Alex said, lean on the naughty libraries that are using pre/post conditions for invalid user input


one situation I’m thinking of is a core.async/go-loop like the one that takes care of message handling. I think I’d catch AssertionError in that case because I don’t want a combination of a bad message with a naughty library to bring down that part of my system.

Alex Miller (Clojure team)15:03:31

pre/post is simultaneously under- and over-used. It's under-used for true program invariants and over-used for input validation.

🎯 6
👍 1

not sure I know better what we should actually do I our app now 😹 Think we’re going with, catch AssertionError s, in some specific circumstances for now and re-evaluate once we have more data. Thanks for the input!


You could wrap the naughty libraries and catch AssertionError in the wrapper, re-throwing it as an ExceptionInfo with a cause. At least until you can convince the library authors to fix their :pre’s and :post’s.

Ferdinand Beyer19:03:51

> naughty libraries I am wondering what the actual use case is. Are you passing user input directly to libraries that are not designed for user input? Using assertions for argument checks actually makes sense, since it is a programmer mistake to pass invalid arguments to functions.


Today I noticed a bug in a 7 year old Clojure project. I bumped Clojure from 1.5.1 to 1.10.3 and started the process. Everything ran smoothly, with no breakage. 😍 Fixed the bug and moved on.

🪄 15

I also realized it's been 7 years since I made - a clj/cljs screencast which isn't too outdated either. Such a blessing jumping off that churn train, and using a stable programming language and environment.

☝️ 13
Fredy Gadotti12:03:55

Hey Magnars! What amazing work you did! Kudos 🥳


Thanks, Fredy! 😄

partyparrot 1

Hello everyone. What’s the best way to use malli for generative testing?

Joshua Suskalo21:03:37

#malli might be a good place where you'll find people good with malli

👍 1

Didn’t realize there was a malli, channel, thanks


me either! there’s so many golden gems to be found 🙂


interesting difference a coworker just noticed:

❯ java -version
openjdk version "1.8.0_322"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_322-b06)
OpenJDK 64-Bit Server VM (Temurin)(build 25.322-b06, mixed mode)

❯ clj -M -e "(println (.getPackage (class (proxy [Object] []))))"
# vs java 17:
❯ java --version
openjdk 17.0.1 2021-10-19 LTS
OpenJDK Runtime Environment Zulu17.30+15-CA (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM Zulu17.30+15-CA (build 17.0.1+12-LTS, mixed mode, sharing)

❯ clj -M -e "(.getPackage (class (proxy [Object] [])))"
#object[java.lang.Package 0x4aa3d36 "package user.proxy$java.lang"]
This difference seems to be causing some pain due to liquibase logging and we are confused why this would be different


getting an NPE because liquibase assumes that clazz.getPackage.getName seems to always be defined:


I think liquibase is wrong


yeah, the method even says it can return null

Cam Saul23:03:04

Hmmm > Gets the package of this class. > > If this class represents an array type, a primitive type or void, this method returns null. A little ambiguous but you could definitely interpret this as meaning that for normal classes it does not return null


Nice find. There is a ticket created to fix the issue in Liquibase now:

Noah Bogart22:03:08

This might be a silly question but are uberjars deployable, for lack of a better term? Which is to say, I create an uberjar locally and then rsync it to a server and it runs correctly


yup. the only issue might be that there is a different version of Java on the server you’re rsyncing to

Noah Bogart22:03:15

Excellent, thank you


oh, also, the uberjar might be using resources like config files, which you would also have to rsync over

Noah Bogart22:03:31

That's a great point

Noah Bogart22:03:02

Right now I'm ssh-ing in and then git pull, lein uberjar, and that's a real pain in the butt lol

Joshua Suskalo23:03:58

anything you get via io/resource will be packaged in the uberjar

Joshua Suskalo23:03:13

any files besides that you read manually will not be in the uberjar and will also have to be rsynced manually

👍 1