clojure

jyn 2025-10-16T19:45:59.209969Z

I want to serialize a clojure data structure to EDN and back. I tried this:

user=> (pr-str {:a/b 1})
"#:a{:b 1}"
but it isn't recognized by clojure -Sdeps, which treats it as a file name:
$ clojure -Sdeps '#:a{:b :1}'
Error building classpath. Extra deps file not found: #:a{:b :1}
what's the intended way to do this? can I tell pr-str to use a simpler format?

p-himik 2025-10-16T19:48:30.009489Z

#:... {...} namespaced maps aren't supported by EDN. But you can change how they're printed:

user=> (binding [*print-namespace-maps* false] (pr-str {:a/b 1}))
"{:a/b 1}"

jyn 2025-10-16T19:48:42.024219Z

oh beautiful, tysm

jyn 2025-10-16T19:49:33.180939Z

that worked!

πŸ‘ 1
oyakushev 2025-10-16T19:55:28.766089Z

Make sure to unbind *print-length* and *print-level* too. I really hate that clojure.edn doesn't have a specialized serialization function and we have to rely on pr which moonlights as a devtime presentation tool.

πŸ‘ 1
πŸ’― 1
πŸ˜… 1
jyn 2025-10-16T19:55:53.873339Z

the good news is that this only runs in CI so if it breaks i will notice very quickly

πŸ‘ 1
oyakushev 2025-10-16T19:57:09.090799Z

Good. I once found out a little too late that my "EDN database" gets truncated at 10 kilobytes.

jyn 2025-10-16T19:57:22.114589Z

oof

jyn 2025-10-16T20:00:10.683729Z

somehow i don't think this is how most people write CI pipelines lol

(defn clj [args]
  (let [deps (binding [*print-namespace-maps* false] (pr-str deps))]
    (str "bash -x $(which clojure) "
         (join-shell (concat ["-Sdeps" deps] args)))))

oyakushev 2025-10-16T20:01:53.238769Z

When it comes to CI pipelines, there could be no judgment. This is a safe space πŸ«‚.

πŸ’― 1
πŸ˜‚ 2
jyn 2025-10-16T20:02:26.295759Z

look at least it deal with shell escaping correctly

jyn 2025-10-16T20:02:33.582289Z

that's better than i can say for 99% of hand-written yaml

oyakushev 2025-10-16T20:06:08.057809Z

I once had to use 16 backslashes to escape one backslash in a very nested cross-tool invocation string. I non-ironically used Emacs' C-1 6 \ to write them out because I couldn't be bothered to count by hand.

πŸ˜‚ 1
phronmophobic 2025-10-16T20:09:54.468599Z

there are actually 6 dynamic vars that influence how pr works https://ask.clojure.org/index.php/14203/how-to-persist-clojure-data-structures-to-disk?show=14205#a14205

☝️ 1
πŸ˜… 2
Alex Miller (Clojure team) 2025-10-16T20:56:10.710549Z

In case you didn’t notice already, -Sdeps takes a deps.edn format so so need a {:deps … } around that

jyn 2025-10-16T21:01:03.899249Z

i'm passing {:mvn/local-repo "./m2"}, which i don't think has a :deps prefix

Alex Miller (Clojure team) 2025-10-16T21:29:25.444619Z

correct, that's top-level

jyn 2025-10-16T20:05:06.777769Z

what is this warning trying to tell me? what should I use instead?

$ clojure -A:test -P
WARNING: Use of :main-opts with -A is deprecated. Use -M instead.
if I just append -M to the end, it still gives the same warning.

jyn 2025-10-16T20:07:01.551809Z

if it helps, i have {:aliases {:test {:main-opts ["-m" "kaocha.runner"]}}} in deps.edn

jyn 2025-10-16T20:08:08.433579Z

oh, using -X instead of -A seems to help

enn 2025-10-16T20:12:31.111419Z

I think it's telling you to do: clojure -M:test -P

☝️ 1
jyn 2025-10-16T20:12:42.907679Z

ohhhh

Alex Miller (Clojure team) 2025-10-16T20:22:39.018619Z

also, you should put the -P before -M or -X or -T

Alex Miller (Clojure team) 2025-10-16T20:22:57.702759Z

everything after -M / -X / -T is an arg passed to the program and will not be sent to the CLI

Alex Miller (Clojure team) 2025-10-16T20:23:09.049499Z

clojure -P -M:test

πŸ‘ 1
jyn 2025-10-16T20:29:41.493729Z

if the clojure CLI runs into an error, it prints the immediate message and then says "Full report at: /tmp/clojure-...edn". How can I tell it to always print the report to stdout/stderr?

igrishaev 2025-10-17T19:46:44.063809Z

Btw, why isn't it a default behaviour: to write a full stack trace to stderror? Just today I spent a lot of time investigating an issue: building an uberjar fails on CI, and it only says: see full report in /tmp/foobar123.edn.

Alex Miller (Clojure team) 2025-10-17T20:29:38.238999Z

Because it’s usually more annoying than useful during dev

borkdude 2025-10-17T20:35:00.307059Z

I know this isn't going to happen but on CI the reverse is annoying and CI's do happen to have the CI env variable set to true... so there's maybe an opportunity to flip the default there

Alex Miller (Clojure team) 2025-10-17T20:36:12.348709Z

At this point, can’t flip the default

jyn 2025-10-16T20:30:23.886439Z

ah, it's --report

jyn 2025-10-16T20:32:52.373349Z

hm no that breaks other things, it starts treating -Sdeps as a file path

lread 2025-10-16T20:52:21.124809Z

$ clojure -M --report stderr -e '(/ 1 0)'
{:clojure.main/message
 "Execution error (ArithmeticException) at user/eval1 (REPL:1).\nDivide by zero\n",
 :clojure.main/triage
 {:clojure.error/class java.lang.ArithmeticException,
  :clojure.error/line 1,
  :clojure.error/cause "Divide by zero",
  :clojure.error/symbol user/eval1,
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type java.lang.ArithmeticException,
    :message "Divide by zero",
    :at [clojure.lang.Numbers divide "Numbers.java" 190]}],
  :trace
  [[clojure.lang.Numbers divide "Numbers.java" 190]
   [clojure.lang.Numbers divide "Numbers.java" 3915]
   [user$eval1 invokeStatic "NO_SOURCE_FILE" 1]
   [user$eval1 invoke "NO_SOURCE_FILE" 1]
   [clojure.lang.Compiler eval "Compiler.java" 7757]
   [clojure.lang.Compiler eval "Compiler.java" 7712]
   [clojure.core$eval invokeStatic "core.clj" 3232]
   [clojure.main$eval_opt invokeStatic "main.clj" 489]
   [clojure.main$eval_opt invoke "main.clj" 483]
   [clojure.main$initialize invokeStatic "main.clj" 509]
   [clojure.main$null_opt invokeStatic "main.clj" 543]
   [clojure.main$null_opt invoke "main.clj" 540]
   [clojure.main$main invokeStatic "main.clj" 665]
   [clojure.main$main doInvoke "main.clj" 617]
   [clojure.lang.RestFn applyTo "RestFn.java" 140]
   [clojure.lang.Var applyTo "Var.java" 707]
   [clojure.main main "main.java" 40]],
  :cause "Divide by zero"}}

Execution error (ArithmeticException) at user/eval1 (REPL:1).
Divide by zero

borkdude 2025-10-16T20:52:29.989639Z

--report stderr but note the order:

clojure [clj-opt*] -M[aliases] [init-opt*] [main-opt] [arg*]

borkdude 2025-10-16T20:52:42.408179Z

report is part of init-opt and -Swhatever is part of clj-opt*

πŸ˜“ 1
πŸ˜… 1
lread 2025-10-16T20:53:34.312139Z

I never remember ordering either, I always have to do a clojure --help

borkdude 2025-10-16T20:54:06.270869Z

now I wonder how to change the printing to stderr of -X functions. does that always barf an exception?

jyn 2025-10-16T20:55:11.109039Z

this is quite annoying because it doesn't compose

borkdude 2025-10-16T20:55:11.838719Z

$ clj -X clojure.core/assoc 1 2 3 4
Execution error (ArityException) at clojure.run.exec/exec (exec.clj:88).
Wrong number of args (1) passed to: clojure.core/assoc--5502

Full report at:
/var/folders/j9/xmjlcym958b1fr0npsp9msvh0000gn/T/clojure-11315834231884795333.edn

jyn 2025-10-16T20:55:34.267219Z

i want to tell a shell script "use clojure --report stderr when running clojure" and that breaks because now i can no longer use -M:test in the script

borkdude 2025-10-16T20:56:07.065339Z

$ clj --report -X clojure.core/assoc 1 2 3 4
WARNING: Implicit use of clojure.main with options is deprecated, use -M --report -X clojure.core/assoc 1 2 3 4
{:clojure.main/message
 "Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).\nclojure.c
$ clj -X --report clojure.core/assoc 1 2 3 4
Key is missing value: 4
Hmm @alexmiller - how to do this for -X ?

Alex Miller (Clojure team) 2025-10-16T20:57:00.719869Z

Use the system prop version with -J

πŸ‘€ 1
πŸ˜… 1
lread 2025-10-16T20:57:48.315989Z

-J-Dclojure.main.report=stderr

Alex Miller (Clojure team) 2025-10-16T20:58:19.023929Z

https://clojure.org/reference/repl_and_main#_as_launcher for docs

borkdude 2025-10-16T20:58:24.604149Z

$ clj -J-Dclojure.main.report=stderr -X  clojure.core/assoc 1 2 3
Key is missing value: 3

jyn 2025-10-16T20:59:20.675179Z

oh the -J version does compose quite well, thank you

borkdude 2025-10-16T21:00:02.924499Z

@alexmiller With -X?

clj -J-Dclojure.main.report=stderr -X  clojure.core/assoc 1 2 3

lread 2025-10-16T21:00:36.245439Z

That case is caught and handled as non-exceptional by clojure, I think, @borkdude.

borkdude 2025-10-16T21:01:27.242429Z

oh right...

$ clj -J-Dclojure.main.report=stderr -X  clojure.core/assoc 1 2 3 4
{:clojure.main/message
 "Execution error (ArityException) at clojure.run.exec/exec (exec.clj:88).\nWrong number of args (1) passed to: clojure.core/assoc--5502\n",

borkdude 2025-10-16T21:01:34.500159Z

βœ…

βœ… 1
jyn 2025-10-16T21:09:42.850569Z

I am using clojure -Sdeps '{:mvn/local-repo "cache/m2"}' to use a local cache instead of a user-global cache. most of the time this works. but if I use -T instead of -X/A/M, it ignores the -Sdeps flag and uses the global cache anyway. What am I doing wrong?

borkdude 2025-10-16T21:21:53.040949Z

Just checking: make sure to write -Sdeps before -X etc

borkdude 2025-10-16T21:22:48.552399Z

This works on my machine, it starts re-downloading deps for a project I already have run before:

clojure -Sdeps '{:mvn/local-repo "cache/m2"}' -X clojure.core/assoc 1 2 3 4

2025-10-16T21:22:59.892069Z

doesn't -T ignore all deps?

borkdude 2025-10-16T21:23:15.918479Z

oh sorry, misread...

borkdude 2025-10-16T21:24:00.564609Z

This also downloads stuff on my machine:

clojure -Sdeps '{:mvn/local-repo "cache/m2"}' -T clojure.core/assoc 1 2 3 4

jyn 2025-10-16T21:26:22.893339Z

I'm testing with clojure -Sdeps '{:mvn/local-repo "cache/m2"}' -T:build native. If I run that twice in a row, nothing gets downloaded. If I delete ~/.m2 and rerun it, it downloads again.

jyn 2025-10-16T21:26:35.820779Z

this assumes a file named build.clj in the current directory and a deps.edn with {:aliases {:build {:ns-default build}}}

borkdude 2025-10-16T21:27:09.732339Z

ok, let me try...

borkdude 2025-10-16T21:27:28.718189Z

still downloads lots of stuff here!

borkdude 2025-10-16T21:27:41.071969Z

it creates cache/m2 anew

jyn 2025-10-16T21:27:53.484259Z

hm

jyn 2025-10-16T21:27:57.174229Z

let me make a minimal repro

borkdude 2025-10-16T21:28:00.356379Z

do you also have mvn/local-repo in your deps.edn perhaps?

jyn 2025-10-16T21:28:06.254829Z

i do not, no

borkdude 2025-10-16T21:28:09.368889Z

(even though -Sdeps should overwrite)

borkdude 2025-10-16T21:28:47.138299Z

is "clojure" aliasing something else on your machine, perhaps with the -J-D prefix?

jyn 2025-10-16T21:29:22.798399Z

(let me make the repro, my current setup is extremely complicated and i don't want to give unrelated details)

jyn 2025-10-16T21:35:53.951799Z

ok yeah -T is unrelated

jyn 2025-10-16T21:35:54.663709Z

hm

jyn 2025-10-16T21:39:46.245339Z

ohhhh this is happening inside create-basis

jyn 2025-10-16T21:39:47.335099Z

jeez

jyn 2025-10-16T21:44:02.743749Z

is there a way to tell create-basis to inherit the -Sdeps from the command line?

borkdude 2025-10-16T21:44:28.133039Z

no

☹️ 1
borkdude 2025-10-16T21:45:10.595399Z

Well, maybe with some scripting yes.

user=> (System/getProperty "clojure.basis")
"/Users/borkdude/.clojure/.cpcache/582082103.basis"

πŸ”₯ 1
borkdude 2025-10-16T21:45:18.134429Z

you can read that thing and there is probably your local/repo

jyn 2025-10-16T21:46:26.631479Z

hm, that will probably be the basis for the currently executing code, right? I have different :deps for my build.clj and my main executable

borkdude 2025-10-16T21:46:27.682259Z

yeah it's in there

jyn 2025-10-16T21:46:41.159159Z

oh i see you mean :basis-config

borkdude 2025-10-16T21:46:50.281529Z

yes, it's the basis of this runtime, but you invoked that one with -Sdeps, which is what you were asking

πŸ‘ 1
borkdude 2025-10-16T21:47:10.752529Z

so you pull out the mvn/local-repo from that file and set it in your build

jyn 2025-10-16T21:49:35.825129Z

that worked!

πŸŽ‰ 2
jyn 2025-10-16T22:25:26.367809Z

ok passing this through a bunch of bash scripts is quite annoying and i can't seem to get access to the args to clojure itself, only the args to my script πŸ˜• is there some way to set :mvn/local-repo with an env variable?

jyn 2025-10-16T22:26:01.200559Z

maybe i will just bypass a bunch of this code in CI tbh