Fork me on GitHub

I think that clojure package on Fedora is broken


A simple clojure -Sdescribe gives Execution error (FileNotFoundException) at ( -Sdescribe (no such file or directory)


clojure team does not maintain any packages besides those listed on


Same happens with clojure -M:env/dev or whatever. Running plain clojure drops into repl with Clojure version 1.11.1


Ah what a shame, I suppose I should report this upstream in Fedora

Bob B12:05:20

from a quick experiment (docker run fedora; yum install clojure), it appears that the package that comes from that is not aiming to be the clojure CLI, but basically just a script that creates a java command line with clojure.main

Alex Miller (Clojure team)12:05:46

Some distributions have a clojure script that is an old thing with no relation to the Clojure CLI


Took a short break from contracting to launch: Clojure backend. Alpha, so buyer beware. Crafted with love in London while juggling 3 kids :)

🎉 12

You may want to post this in #C06MAR553 :)

👍 2

I actually just deleted it from #announcements (without seeing your suggestion here) -- seems like a commercial project rather than eg an OSS lib. Am I misunderstanding from my relatively quick look at it?


Are those projects not allowed in #announcements? Wouldn't be the first time. I never read a restriction on OSS only


Yeah, I guess the subject line in #C06MAR553 is ambiguous. I'll post in <#G050G0K5D|>, let's see what other people's impressions are.


@U0D2WT5HR Already found #C023JSWHY2D which was created for posts like this -- we should probably promote it more...

👍 2

Are custom domains supported/planned? Sounds like a nice solution to do micro-blogging


Yes. This is going towards self hosting in fact. Codebase was designed with that usecase in mind. But this has a very different threat model, so I’m starting with this for now

👍 4

Would u believe it if I said its powered by … SQLite )))


> Yeah, I guess the subject line in #announcements is ambiguous. I'll post in <#G050G0K5D|>, let's see what other people's impressions are. Outcome in case anyone's curious: we'll write this up in more detail for the subject line in #announcements, but the tl;dr is that that channel's for Clojure/Script OSS (typically libs) and/or tools made for incorporation into Clojure/Script projects. OK, back to hearing about @U0D2WT5HR’s cool project!


Made it to front page of Hacker News fwiw. Look for title Show HN: Git Hooting

🎉 2

An internal tool I'm writing runs CI jobs based on a set of rules. An explicit design goal is clear separation between configuration (the business rules) from code (which should be ignorant of the rules). What are good ways of doing that with Clojure? • External DSL: The rules are stored as data in an EDN file. But I'd need to write an interpreter for the DSL, and a simple DSL is not composable. • Internal DSL: Instead, I could store the rules as code. This gives me composability and abstraction for free. But what should the config-as-code look like? If I load-file a separate config.clj, how can the config-as-code integrate with my tool, e.g. call back into my code? Any good examples to look at?


I guess another way to phrase the question is, how do I create a clear module boundary around the tool (vs how it's actually used) and make sure that "mere configuration" doesn't bleed into the code


maybe deps.edn (config) and (builds are programs) is a nice example?


although that's maybe the opposite example: there's no rules in config, rules are in code


Yeah, nice examples • deps.edn is an external DSL (custom interpreter) • build.clj as used by is an internal DSL (integration via (:require [ :as b]))


I think the word DSL is a bit overloaded here.


And the intro paragraph from the guide gives a nice rationale: > The philosophy behind is that your project build is inherently a program - a series of instructions to create one or more project artifacts from your project source files. We want to write this program with our favorite programming language, Clojure, and is a library of functions commonly needed for builds that can be connected together in flexible ways. Writing a build program does take a bit more code than other declarative approaches, but can be easily extended or customized far into the future, creating a build that grows with your project.


Hey team, question for you, about a concurrency-related test I am trying to write:

(deftest partitioned-items-are-processed-one-at-a-time
  (let [input-ch (a/chan)
        results (atom [])
        f (fn [v]
            (Thread/sleep 10)
            (swap! results conj v))]
     {:partition-buffer 4 :timeout-ms 1000}
     input-ch identity f)
    (doseq [x [:job-1 :job-1 :job-2 :job-3]]
      (a/>!! input-ch x))
    (a/close! input-ch)
    (Thread/sleep 100)
    (is (= :job-1 (last @results)))
    (is (= [:job-1 :job-1 :job-2 :job-3]
           (sort @results)))))
I have a dynamic-pubsub system, which takes an input-ch and partitions items to one worker per partition. Here, I wanted to test that jobs are in fact processed one at a time. To do this, my idea was to give work like:
[:job-1 :job-1 :job-2 :job-3]
(Here the partition key is identity) Because :job-1 came in twice, I would expect that the other workers would get their jobs done before the second job-1 is procesed. Therefore job-1 should come at the end. This is indeed what happens, but it's a indeterminate right now. Plus I don't like that I have to have thread/sleep. Would you test this in a different way?

Ben Sless15:05:29

Does closing the input channel result in clean shutdown of what's returned from dp/start ? can it be waited on?


Hey Ben! If input-ch closes, I close all the partition channels. I don't return anything from dp/start though; this is mainly because the f in this case is supposed to mainly produce side effects.

Ben Sless19:05:01

You need to wire up a mechanism that lets you wait on dp finishing, such as a channel that will close once all workers are done. That way you can close the input, wait on the promise returned by dp and have a guarantee it finished cleanly


I could do that! Maybe the flow can be: I have an out-ch. When input-ch closes, I'll merge all partition-ch into out-ch, Then I'll close every partition-ch. (Inspired by your group-by!) If I did this, how would I use it to improve the test?

Ben Sless19:05:27

Return out ch Put all inputs on input chan Close input chan Then wait on out to close with a blocking take No need to sleep


One issue for that, is for this system I have a partition-buffer This is to address the following issue: • I want each worker to work ~independently • But the "scheduling" go loop will block when I >! into a partition-ch • So, I let each partition-ch run ahead by some partition-buffer But, if I were to start up the input ch, and quickly close it, then it may be that some jobs are still in the partition and haven't been done yet -- which would potentially break the test. I guess I could write a test of something like: I expect that job-1 is still in the "list of jobs to be done". The other issue is, it would still be indeterminate. For example, :job-2 for some odd reason could still take so much time that the worker for job-1 handles two tasks for job-2's one task. (This felt unlikely to me, but I started noticing the test was flaky during ci) For more context, here's the current implementation of dynamic-pubsub: Really appreciate your thoughts Ben! You and hiredman definitely helped inform this ns.

Alexander Kouznetsov16:05:26

Is there a tool that given a full function name can find all namespaces that transitively may invoke that function?


LSP (or clj-kondo) probably...?


it depends what you mean by all those things 😕


kondo does static analysis and is generally pretty good, but not complete


the alternative is loading the code and poking around at it in memory


but functions are not inherently named, the same function object can be bound to many different names (locals and vars in namespaces) so that is a thing


likely you mean something like, given a var find all usages (and maybe do the transitive closure of that)


kondo is prettyy good at that, but I haven't seen anything that does the transitive closure bit if that is what you want

Alexander Kouznetsov16:05:07

> likely you mean something like, given a var find all usages (and maybe do the transitive closure of that) Yes, mainly that. > but I haven’t seen anything that does the transitive closure bit if that is what you want 😞

Ben Sless17:05:20

If you put the analysis output in a datalog or graph database the transitive closure is trivial See Or my abortive experiment

Brian Beckman16:05:37

SOLVED Hello friends --- another macro puzzle from me. Here is a hand-written defmethod:

(defmethod ->asdl :masr.specs/symbol
  [#:masr.specs{:keys [term asr-symbol-head]}]
  (symbol->asdl asr-symbol-head))
Here is a macro-expansion (macro presented immediately below) from CIDER C-c RET:
  [#:masr.specs{:keys [term asr-symbol-head]}]
  (symbol->asdl asr-symbol-head))
They look identical to my eyes (except for some harmless crlfs) Here is the attempted macro :
;; Doesn't work for unknown reasons. Expansion seems
;; identical to hand-written defmethod.
(defmacro term->asdl [term]
  (let [ns "masr.specs"
        ;; like ::keys
        keys-key (keyword ns "keys")
        ;; like ::symbol or ::stmt; values of term
        mthd-key (keyword ns term)
        ;; like asr-symbol-head or asr-stmt-head, a key-symbol
        ;; for destructuring
        nest-ksm (symbol (str "asr-" term "-head"))
        ;; like symbol->asdl or stmt->asdl
        call-sym (symbol (str term "->asdl"))]
    ;; term below is a constant destructuring key
    `(defmethod ->asdl ~mthd-key
       [{~keys-key [term ~nest-ksm]}]
       (~call-sym ~nest-ksm))))
(term->asdl "symbol") ;; C-c RET here to expand
Here is the error message from the console (not from CIDER) Syntax error macroexpanding clojure.core/fn at (masr/specs.clj:1914:1). ({:masr.specs/keys [masr.specs/term asr-symbol-head]}) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list {:masr.specs/keys [masr.specs/term asr-symbol-head]} - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list I’ve been unable to understand why the expanded version fails the syntax-check for fn but the handwritten version (which looks identical to me) works. I don’t know any other debugging strategies for this. Any advice?

🏁 2

The macroexpansion seems wrong. Notice that term in your macro. It's most outer form is under the syntax quote, so that term actually becomes current-ns/term.

👍 2


Clojure 1.11.1
(defmacro term->asdl [term]
  (let [ns "masr.specs"
        ;; like ::keys
        keys-key (keyword ns "keys")
        ;; like ::symbol or ::stmt; values of term
        mthd-key (keyword ns term)
        ;; like asr-symbol-head or asr-stmt-head, a key-symbol
        ;; for destructuring
        nest-ksm (symbol (str "asr-" term "-head"))
        ;; like symbol->asdl or stmt->asdl
        call-sym (symbol (str term "->asdl"))]
    ;; term below is a constant destructuring key
    `(defmethod ->asdl ~mthd-key
       [{~keys-key [term ~nest-ksm]}]
       (~call-sym ~nest-ksm))))
user=> (macroexpand-1 '(term->asdl "symbol"))
(clojure.core/defmethod user/->asdl :masr.specs/symbol [#:masr.specs{:keys [user/term asr-symbol-head]}] (symbol->asdl asr-symbol-head))

🏁 2

We have a lot of tools for thoughts. Some facilitate thinking, some just make your head heavy. :) Seems like this particular tidbit of Cider behavior lands in the latter category by "simplifying" user/term to term because the current ns is user.

👍 4
🐛 2
metal 2
Brian Beckman16:05:31

Thanks much!

👍 2
Noah Bogart19:05:12

In by @timothypratley, the author mentions speaking with @alexmiller , and how Alex wishes that "tool makers would adopt the error reporting features in Clojure 1.10+". As an author of a tool, i'd love to hear more about this. How do you propose tool makers use the


probably related to how tooling like Cider (maybe all nrepl ones) by rendering custom errors sometimes end up hiding some useful error info added in clojure 1.10

👍 2
Alex Miller (Clojure team)19:05:11

yes, that basically. we don't think you should see a stack trace when an error occurs (unless you ask for one) and the clojure.main repl does not (has never) done so, but many of the tools still do that. we also created a bunch of error triage and formatting in 1.10 and those are still not being used in some tools

👍 3
Noah Bogart19:05:02

Ah I see, tools interfacing with clojure, not tools written in clojure (such as linters). Cool, that makes sense, thanks


I've grumbled about this multiple times in multiple threads here 🙂 Usually someone bewildered by a CIDER stacktrace that when the same code is evaluated in a plain REPL produces better output...

👍 4
Noah Bogart19:05:11

Yeah in this context I've seen the same and experienced myself!

Alex Miller (Clojure team)19:05:32

here's some example things that make errors, compare these in the Clojure CLI vs your favorite tool: • :::5 - error during reading • (let [x]) - error during macroexpansion caught by a macro spec • (cond 1) - error during macroexpansion thrown by the macro • (defmulti 5 class) - unexpected error during macroexpansion • (def 5) - compilation error • (/ 1 0) or (+ 1 :a) - evaluation error • (deftype T [a]) (defmethod print-method T [_ w] (throw (Exception. "boom"))) (->T 1) - printing error

👀 2
💯 2
🙏 1
Alex Miller (Clojure team)19:05:52

if you find your tool to be less good, please let your friendly tool maintainer know :)

clojure-spin 2
Noah Bogart19:05:45

magnificent examples, thank you

Alex Miller (Clojure team)19:05:57

the other place this pops up is in clojure.test error reporting, which can easily dump a lot of garbage, and I have a placeholder ticket to look at that specific problem, but haven't worked on it yet

Alex Miller (Clojure team)20:05:43

also of note, the clojure.main error reporting stuff is broken into parts and you can use the triage function to get the analysis of an error as data, then print it out or display it in a gui in a way of your choosing, you don't have to use the message printer parts

Alex Miller (Clojure team)20:05:34

user=> (try (read-string ":::5") (catch Throwable t (-> t Throwable->map clojure.main/ex-triage pprint)))
#:clojure.error{:class java.lang.RuntimeException,
                :line 1,
                :cause "Invalid token: :::5",
                :symbol user/eval153,
                :phase :execution}

👍 2
Alex Miller (Clojure team)20:05:18

that's maybe a confusing example b/c you really need to be in the Read/Eval/Print of the REPL to catch these for reals


In Portal, I see:


(and I can expand that for a stacktrace if I really need to)

Alex Miller (Clojure team)20:05:29

expansion is a great tool in a ui selective unhiding and user control


I have a hot key that cycles through viewers so I can look at an exception like this too (plus other, more raw, expansions):


Great question, very helpful examples. Many thanks for these. 👍 (Tutkain didn’t behave correctly with regard to :read-source and :print-eval-result.

gratitude 4

Have you considered creating an issue with CIDER?


Full marks for issue crafting! 👍

🙏 2

I wonder how much of that bleeds into CIDER/Orchard and nREPL and shows up in other editors/IDEs that use CIDER-derivative code? (I don't know because I don't have Calva's REPL output window open -- I use Portal -- and I have a hotkey bound to a tap> expression showing more detail about *e when I need it (otherwise Portal and its nREPL middleware show me just the summary, by default, like the bare CLI REPL for the most part).


I actually find the stack traces from CIDER useful, and most of the time you can filter out noise by selecting options to exclude parts of the trace. If anything, the least useful error messages to me are the spec validation failures since they’re terse and cryptic compared to exceptions with data written by a human. For reference, this is what I see in CIDER when I decide to only show frames relevant to my project (i.e. hide clojure and nrepl internals)


For reference, here is what a spec validation error looks like; it’s much harder to understand than Malli’s explanations and in general is very “not human readable” IMO. From the error message alone, I cannot guess what went wrong; it takes me less time to diagnose the code producing the spec validation error than to diagnose the data from the exception.

Noah Bogart20:05:19

That's unreadable because it's showing the whole stack trace. in vim (using conjure), i see:

; eval (root-form): (let [x])
; (err) Syntax error macroexpanding clojure.core/let at (src/noahtheduke/splint/rules/helpers.clj:342:1).
; (err) [x] - failed: even-number-of-forms? at: [:bindings] spec: :clojure.core.specs.alpha/bindings


That is not a stack trace. That is data outputted by clojure.spec


It’s the equivalent of seeing data from s/explain


I’ll note the REPL does print the s/explain message, but the exception data does not include it anywhere and instead provides an overwhelming amount of information that the user likely will not understand

user> (fn [{:keys :keys}] 456)
Syntax error macroexpanding clojure.core/fn at (*cider-repl ~/Desktop:localhost:54761(clj)*:1:7).
({:keys :keys}) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:keys :keys} - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list
And again, it is not a java stack trace. It is data that Clojure produced as a result of the spec validation failing

Noah Bogart20:05:58

"Unhandled clojure.lang.Compiler$CompilerException" is an exception. your repl output is correctly showing you only the massaged output. the exception does contain that information: {:clojure.spec.alpha/problems ({:path [:fn-fail :arity-1 :params] :reason "Extra input" ... :val ({:keys :keys})} ...) ...}

Noah Bogart20:05:38

and then the clojure.main functionality is turning that into the output you see in your repl but don't see in CIDER


It’s an exception, but not a java stack trace. It’s the result of throwing ex-data and providing the user data, though again, it is an overwhelming amount of data and in general s/explain is cryptic compared to e.g. malli’s explainers (especially if you turn the explanations into human-readable errors in malli with me/humanize) It took me longer decoding the data from spec than it did looking at the fn form and seeing exactly what’s wrong

Noah Bogart20:05:07

it seems we're talking past each other at this point, so i won't press the issue


I don’t think so? The CIDER REPL seems to print the exact same thing as clj does

% clj
Clojure 1.11.1
user=> (fn [{:keys :keys}] 456)
Syntax error macroexpanding clojure.core/fn at (REPL:1:1).
({:keys :keys}) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:keys :keys} - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list


Still doesn’t really address my point that the messages are cryptic and the ex-data thrown from clojure.spec is overwhelming and even harder to read. CIDER can probably choose to hide ex-data when it comes from clojure.core macros and functions themselves, but I consider that a loss of functionality IMO.


I think those are two different things. The original discussion was about the default behavior for tooling like cider, being it used like it is recommended, by evaluating from your source code instead of pasting on the repl. For the (fn [{:keys :keys}] 456) the simple repl error is much better than the window that popup in Cider, which includes stack traces and everything

☝️ 1

Yeah. I understand the point is that CIDER’s error window is often confusing, especially if you do not hide internals. But how do you decide when to hide it? The REPL does output the improved error messages from Clojure 1.10+ it seems, but again, sometimes it is useful to see the exceptions, and I dont know of a good solution to this. My discussion on spec validation errors is that, when Clojure decides to use them, the error message are hard for me to understand. With enough time, I can guess what the spec is saying, but it’s still hard.

Alex Miller (Clojure team)20:05:32

ex-data is sometimes useful, and sometimes overwhelming (spec is but one example). we debated a long time on whether to show it by default in clojure.main repl and decided it is not (but is easily retrieved by for example (-> *e ex-data pprint) if you need it)


FWIW, in my VS Code/Portal setup (using CIDER under the hood, but also the Portal middleware), when I eval (fn [{:keys :keys}] 456) I see the following:

👍 1

The bottom window is the "log" style output from the Portal middleware. Then I ran ctrl+alt+space o which tap>s the :stdio part of the log data -- the raw REPL output -- which appears in the top window.


What does Portal do when a 3rd party library decides to throw ex-info? Does it omit the ex-data or is there a heuristic to decide when it’s useful to present to the user?


I guess there are many ways of looking at errors for experienced Clojure users, I think the spirit of the original post had to do with the basic tools that newcomers are going to try, in their default configs, and how simple repl sometimes is better at this than more advanced tools like Cider


I also have ctrl+alt+space e bound to code that tap>s the triaged version of the last exception and the exception data (hash map) itself -- which Portal represents in a custom way -- which I can then expand (once, or twice, depending on how much detail I want):

👍 1

I guess regarding CIDER: I think hiding internals by default is a good first step, for both beginners and experienced users. But given that the REPL already prints improved error messages, the problem with CIDER seems to be the way it presents exceptions. For now it just unconditionally opens a temporary window with the exception data, but it does look like Portal is doing “the right thing” here 🙂 Thanks for the demo @U04V70XH6


One more question if you don’t mind: is this just a Portal thing, or does Calva make use of it, too?


@U0739PUFQ I can cycle through expansions to see the ex-data:


@U0479UCF48H The raw Calva REPL produced this from the spec failure:

; Syntax error macroexpanding clojure.core/fn at (components/uuid/src/ws/uuid/impl.clj:143:1).
; ({:keys :keys}) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
; {:keys :keys} - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list
It does not show a stacktrace.

👍 2

(I just keep the REPL hidden in general and rely on Portal, which I can mostly drive from the keyboard, while staying in my editor)


@U04V70XH6 yeah I know, I'm just saying that your setup is pretty involved, I guess its is nice if someone is starting with Calva, but anyway it requires to setup a different 3rd party tool, and some extra understanding just to see errors a little cleaner


@U0739PUFQ The default Calva REPL behavior is mostly pretty good. It's nrepl.cmd-line with CIDER middleware added (so it's the CLI REPL, essentially). And inline evaluation summaries (which I assume come from nREPL).

Noah Bogart20:05:30

i believe in most tools that rely on the clojure.main functionality, they also support showing the full stack trace through other means. i showed the output of conjure when it has such an error, but I can also press \ve to show the last error (effectively running *e through conjure) to show the full stack trace.


@U04V70XH6 yeah maybe the cider error window is the only one that is confusing

Alex Miller (Clojure team)20:05:09

Cursive used to have the issue with showing the stack trace, but I think I complained enough to Colin that it doesn't now :)

😂 3

I tried helping a friend get set up with Cursive but I recall seeing huge stack traces being printed in the REPL when something went wrong. I’ll have to try it out again. I like recommending Cursive to beginners because you can just click 2-3 buttons to properly set up a Clojure project for building, REPL, and running. Besides gigantic stack traces printed into the REPL and hiding my code, I enjoyed the whole Cursive experience and I still think they offer the most beginner friendly way to set up Clojure projects


Cursive's great if someone has a background with IDEs (perhaps coming from a Java background). Calva also has a great getting started experience, for folks coming from other backgrounds.


This is probably derailing the thread, but I’ll post it anyway: I first tried getting a friend started with Calva. It was also his first introduction to VS Code. But the introduction in Calva’s website did not really help him at all. There was a “Getting started” page but it didn’t exactly tell us how to create a Clojure project, and it was loaded with technical terms like jack-in, which confused my friend. In the end, IntelliJ’s interface was easier for him to navigate, because he could just click “New project”, select “Clojure”, click “Next”, click “Finish” and get started right away. With that said, Calva is very nice if you’re already used to VS Code; I just wish there were more beginner friendly documentation around for it. Or at least, have a tutorial that first teaches people how to create a Clojure project in VS Code


same thing happened to me some days ago, it wasn't straight forward for me to setup Calva, but I had never used VSCode before


@U0479UCF48H When did you last try to onboard a beginner with Calva? The Getting Started experience has improved dramatically over the last year... including providing a couple of Getting Started projects to explore Clojure with.


At the start of February this year. The easy part was installing VS Code and Calva. The hard part was following the documentation because it seems to assume you already know how to make a Clojure project (and you’re comfortable reading Clojure jargon)


Fair enough. There's been quite a bit of talk of how best to allow for Calva to create a new project -- either a minimal one or an app/lib one based on deps-new...


@U0ETXRFEW Might have an update on where that discussion/thinking has gotten to these days...?


If you’d like, we can start a new thread on this. Because I do want to make Clojure as easy as possible to get started with. (Yes, I know I use Emacs, but it’s because old habits die hard 😂 ) In the case of Calva, I think it’d be nice showing how to use deps-new in the terminal, or instructing users to start with an empty deps.edn file (i.e. start with just {}, no dependencies or aliases or anything), so they can start a REPL and add deps to it later, then they can learn tools.deps stuff in a different tutorial


I can't remember whether this discussion was in #C02CPM2R1KK or in #CBE668G4R directly (and I think it's been a few months since then).


Calva doesn’t assume you know how to start a new Clojure project. But, sure, it doesn’t tell you how to do it, or help you with it. We haven’t quite figured out how to do that best. It is something I think about a lot and something I really hope to have addressed soon. A thread in #CBE668G4R about it would be great. Or as a Discussion thread on the Calva repository. I really need help with thinking about it. Maybe we can take some inspiration from Rich Hickey’s Conj talk.

Drew Verlee09:05:35

@U0479UCF48H have you tried expound? It makes spec errors into more human readable messages.