Hacker News new | ask | show | jobs
by BaculumMeumEst 1220 days ago
I love Clojure the language but I’ve never seen a more fragmented ecosystem.

There seems to be a pattern in the language of “a problem emerges > a community solution gains traction > Cognitect develops their own solution but its weird and undocumented”, like deps.edn over leiningen, spec over malli, pedestal over ring, etc.

Many prominent clojurists recommend deps.edn over leiningen and socket repl over nrepl, but I’ve seen very little guidance on how either actually work or how to use them.

Spec seems kind of weird and not well thought out either.

And Clojure CLI tools also seem like a total shitshow compared to go or rust’s tooling.

As a result working with Clojure feels puzzling and unpleasant, and I feel hesitant to use any community library or project in the language.

10 comments

I think the docs are pretty good, but the main "problem" is the same problem as anything else in Clojure: you have a lot of options, and maybe even documentation, but not a lot of guidance. Which I don't think the community broadly views as much of a problem, but it does make it difficult for new people getting into the language.

There are some within the community working on this but unless it becomes a blessed solution it can still be kinda difficult for new developers to onramp into the language.

E.g. compared to something like Elixir, the road is much more arduous with Clojure.

The problem with things like spec is it kills interest in other libraries solving the same problem. Note that Malli released after spec, and after spec all but killed off a few spec-like libraries, my general observation seems to be that many in the community have moved on from spec.

I think your pattern is right except for "its weird and undocumented" - I have found the maintainers are superb at docs -

e.g. deps.edn -

there is a "Guide" https://clojure.org/guides/deps_and_cli

there is a "Getting Started" https://clojure.org/guides/getting_started (it's branded as an overall Clojure getting started but the core of deps is just the clj/clojure command line tool you would use to install clojure)

there is a deep "Reference" with a "Rationale" https://clojure.org/reference/deps_and_cli

spec

Spec Guide - https://clojure.org/guides/spec

Spec Rationale https://clojure.org/about/spec

Spec API reference https://clojure.github.io/spec.alpha/

Spec resources (brief) https://clojure.org/community/resources#spec

(Rich Hickey also gave a couple of spec talks that are on YouTube)

Also, for my money, deps.edn is way more composable than lein, does not feel weird to me at all. Ditto for spec vs prismatic schema which preceded it. I think the latter case is one where people were more miffed that the official solution displaced a community beloved tool, but to me it genuinely seems really well thought through. There are some flaws discussed in the Rich Hickey talk "Maybe Not" which led to an experimental (still beta) spec2 but spec1 I still find miles ahead of what's available for the languages I've worked with.

"Superb" is not the word for deps.edn documentation. It is certainly documented but the gap between most Clojure documentation and the deps.edn documentation page is big. I suspect Rich wrote most of it but left the deps.edn page to someone else.The deps pages have a knack for includng pages of waffle that doesn't help to solve the current problem.

If a newbie is trying to debug

    {:deps
     {ring/ring-devel {:mvn/version "1.9.6"}}
     {ring/ring-core {:mvn/version "1.9.6"}}}
=> Error building classpath. Error reading edn. Map literal must contain an even number of forms

They have to notice that while they got 80% of their maps right, the file itself if a map with 3 forms. This is compounded by the fact that there are no examples of a multi-dependency project in the getting started page! If you know Clojure, this is an easy puzzle. For rookies, they didn't use (map) so the error message will probably trick some percent of them.

I don't think the deps documentation has been validated to provide what people actually want to know. Technically it is probably thorough, but "Superb" is a high bar it does not reach.

Probably the proper approach is learning deps by copying other people. Trying to learn it from the docs didn't work for me (it did for the rest of Clojure, so that was an unpleasant change).

These are useful comments and issues are welcome at https://github.com/clojure/clojure-site/issues
smoothly changes gears ... although of course there can be no question that the deps documentation is managed by a man of patience, talent and extreme skill to whom we are all thankful.

Sorry, I had a bad time learning deps and the frustration still haunts me.

> a multi-dependency project in the getting started page

That's because they basically don't support monorepo multi-module projects. It's incredibly tedious and makes me long for something like Maven or Gradle (tools that otherwise are often too complicated).

How do you mean? You can specify local (fs) dependencies fine, which is presumably what you want in that scenario?

(I regularly contribute to a monorepo like this, though admittedly the top-level components are relatively disjoint.)

I mean, you can hack something together with local dependencies, yes, but there's no out of the box way to build and test everything together, or to do things like pin dependency versions across projects, have projects inherit shared configuration, etc. We end up creating a bunch of pseudo-projects that contain things like single dependencies, but this blurs the line between build time concerns and actual source code. As with everything in clojure, you can write it all yourself using something like tools.build, but it's not convenient and clearly not a first class use case.
I wonder if Polylith would be up your alley?

https://polylith.gitbook.io/polylith/

Out of curiosity - what is the correct form in this case?

Ed: I'm guessing a key => list of deps, maybe?

It should be:

    {:deps
     {ring/ring-devel {:mvn/version "1.9.6"}
      ring/ring-core {:mvn/version "1.9.6"}}}
There was one too many map.
I think you might be asking for a type :)
Not really. This is a syntactic error, not a type error.

To expand, this is the equivalent of something like {"a": 1, "b":}. The type checker in something like Typescript wouldn't ever come into play, because it's not legal syntax to begin with.

The parent was asking for the correct form, the syntax of EDN is necessary but not sufficient to specify this. A type (or a schema) is needed. Even if documented manually ad-hoc, that is better than nothing and would stop people needed to search the internet for examples.
Excellent doc references, and FWIW I share your perspective on the tools.
Been using Clojure professionally for 9+ years now and while I'm thankful that I can make a living writing the language and using Emacs all day, I've found these points to be spot on.

These issues have added noise/confusion to what used to be a much simpler ramp-up for new developers. For those who've been around for awhile, the overall experience definitely felt cleaner previously, with more obvious best paths. While it's hard to argue against more options, the effect on the ground is that it's also needlessly divided the already small community a bit too.

I have only used Clojure for side/hobby development, which admittedly makes this a shallow observation.

I've found working with Clojure to be extremely pleasant/joyous and when I've had the need to bring in someone else's code, it's been an overwhelmingly good experience, even if the library is 5 years old and untouched for the last 4. (Find a random node package that's last changed 4 years ago and it might as well be toxic waste. Find a clj lib in that condition and it's probably going to work smoothly.)

I do agree the prevalence of lots of examples of leiningen and fewer examples using tools.deps/deps.edn and clojure cli makes it hard for me, as a beginner, to know the "right" patterns to use.

I think the bottom line with Clojure is it's not an ecosystem well-suited for non-veteran programmers. For as simple as the language is, effectively using Paredit, navigating partially documented libraries, diving into source code to see how things interact—it's tough as a new developer. I don't believe Clojure is overtly hostile to newcomers; it's just crafted by veterans, for veterans. And this is the result.
I don't agree.

I consider myself something akin to a veteran. I've been coding for over two decades. Not sure if that qualifies, but anyway.

My point is: it's been with experience that I've come to value ergonomics the most.

And that for me includes having a thriving and focused ecosystem, extensive industry penetration, good and stable tooling, lots of well known codebases learn from, etc.

It was when I was young and inexperienced that I didn't see those as the important bits. I was happy hacking on any half assed editor exploring undocumented APIs and trying to discover patterns and idioms by myself. I was happy to waste time.

I'm not anymore. That's why, while a love Clojure as a language, I don't really use it that much nowadays.

Too much friction.

If only time served makes you a veteran, I guess I'm one too, but a lot of it was mediocre time.

I still like hacking on things, but only on hobby projects. When it has something to do with work, I agree. Clojure reminds me of why I liked coding in the first place and I like the way LISP-type languages make me think differently about what I'm doing.

But "in the real world," yeah, I don't want those things. I want something I can build and maintain and be done.

Not really.

Our experience is that Clojure is a very good language for newcomers to programming as a profession.

In fact, we almost exclusively hire new computer science graduates. None of them had heard of Clojure before joining, and the vast majority of them became useful in 2 weeks, and become productive in a few months. What you described as barriers are the things that got sorted out in the first day when they join.

We do not hire veterans unless they already know Clojure. These people need to unlearn stuff, some of them are resistant to changes, so we don't bother with them.

> deps.edn

Deps is well documented.

The issue I personally found is that I needed to look at a bunch of OS project's deps.edn to see how people commonly structure things. Other than that it is a simple tool.

> socket repl over nrepl

I personally use Calva (VSCode) which just starts an nrepl based on deps.edn. When writing babashka scripts I start the repl manually and connect to it. Very pleasant experience so far.

> Spec seems kind of weird and not well thought out either.

I didn't like it at first, but once I got that everything is bottom up I had an AHA moment. Function specs and instrumentation are very powerful. Conform is basically a parser, which can give you a lot of leverage.

What bothers me about spec is that it is still not released though.

> Deps is well documented.

> The issue I personally found is that I needed to look at a bunch of OS project's deps.edn to see how people commonly structure things. Other than that it is a simple tool.

This seems like a contradiction, because if it was well documented you wouldn’t need to look at other people’s configs to see how to use it.

My experience with deps.edn is that every time I start a project and make a deps.edn file, I immediately draw a blank and don’t know how to structure it, so I open ones from other projects to start lifting stuff out of them.

I still don’t know how to reliably configure a project to use nrepl or socket repl without just using an editor plugin. I definitely have no idea how to use those in conjunction with a tool like reveal.

To me, none of that is simple. Simple would be like Emacs’ use-package. With that I know how to add dependencies, specify keybinds, and do initialization and configuration off the top of my head knowing only the name of a package I want to use. And it has really nice documentation with tons of examples.

https://github.com/jwiegley/use-package

> My experience with deps.edn is that every time I start a project and make a deps.edn file, I immediately draw a blank and don’t know how to structure it, so I open ones from other projects to start lifting stuff out of them.

My new project deps file is always literally "{}". I love that that's all I need to do to start doing stuff. I add a couple libraries as needed. Maybe at some point an alias or two.

I have found practical.li [1] to be an excellent source of information.

In particular cloning their clojure-deps-edn [2] as my ~/.clojure folder gives me ready made aliases for different kinds of repls, tools like reveal etc.

This, plus polylith [3] architecture has finally allowed me to start working in clojure and not get lost in configuration confusion.

[1] https://practical.li/clojure/clojure-cli/projects/add-librar...

[2] https://github.com/practicalli/clojure-deps-edn

[3] https://polylith.gitbook.io/poly/

The C language is extremely well-documented. It's still helpful to see other people's C code to understand common/useful patterns.
I don't know. I saw tutorials mixing clj -A, -X and -M flags, but what's the difference between them? When do I use each? What's the -r flag do and how do I use it? How do I update my dependencies? How do I build a jar and deploy it?

It just always felt like I was running into things that felt basic and the docs didn't have an answer for them. I would see so many elaborate deps.edn files in people's configs and when I tried to look up how they worked I couldn't find anything.

> I saw tutorials mixing clj -A, -X and -M flags, but what's the difference between them?

That depends how you define your build targets (called aliases) in deps.edn. And there is also -T to call (public) functions in a namespace (file/program), e.g. when using build.clj - that's for building your jar.

-X calls a function that you can define using :exec-fn, :exec-args has it's arguments (usually a map) -M calls the main function ('executes a program', if you will), it's 'normal' (command-line) arguments are passed using :main-opts

And btw. `clj` is just a wrapper around `clojure` that runs `clojure` in rlwrap. No need to use this if you do not use the REPL.

Here is a (big) template I've just finished yesterday: deps.edn: https://github.com/Release-Candidate/Clojure-Template/blob/m...

and it's usage: https://github.com/Release-Candidate/Clojure-Template#build-...

build.clj - build a jar and run mkdocs https://github.com/Release-Candidate/Clojure-Template/blob/m...

Deps.edn is a bit strange but build.Clj was the thing that really felt like Setup.py writing in 2010
Deps may be well documented, but lots of introductory material seems to use lein. Having two systems is more confusing.
Because a decade ago there was only Leiningen -- so all the books and tutorials (and videos) created in the early days of Clojure had to use it. At work, we started with lein (back in 2011 for our production code), then we switched completely to Boot in 2015, and then we switched completely to deps.edn in 2018. build.clj (tools.build) is a "recent" addition (less than two years since the very first 0.0.1 commit).

More and more introductory material is appearing these days featuring deps.edn but given there was a decade of Leiningen usage out there before deps.edn even appeared, the current state of affairs shouldn't surprise anyone.

As for more than one system, lots of languages have that situation: consider make -> ant -> maven -> gradle etc.

How do we ever move the world forward if we are only allowed to have one thing?
Cognitect is pretty actively hostile towards community growth. There's literally no reason they shouldn't have just accepted leiningen as the official build tool.
I can't agree more. Not only is documentation incomplete (in some cases), things are even worse. For example, spec is still slower than plumatic schema (which predates it) and tools.deps/tools.build still needs to catch up to leiningen options.

Most Clojure projects are maintained by a single person - Alex Miller. Even tickets to jira are manually entered by him after you explicitly write a bug request in ask.clojure.org. Quite a closed community; no wonder many significant contributors left for other venues.

I think most programming communities experience fragmentation as they grow. I would say the javascript ecosystem has had an order of magnitude more fragmentation at every layer: package management, server, client, module specification. IMO this is the cost of progress.
True, but the JS community is huge. Sure, not by its own merit, but because of browsers. Still, can afford to be fragmented.

For smaller languages like Clojure the consequences are way worse.

So clojure has three main build tools: Lein, deps, and boot.

Meanwhile in the java world you have: Gradle, Maven, Ant, etc...

> And Clojure CLI tools also seem like a total shitshow compared to go or rust’s tooling.

Wat

> Spec seems kind of weird and not well thought out either.

WAT?

Maybe your comment makes sense to you with all the context and experiences inside your head, but I have no clue what points it is trying to make. Can you maybe use more words to say what you're trying to say here?
Java has more build tools then Clojure, so thus is more “more fragmented” apparently. I replied “wat” as a slang version of “what” and a short version of “what do you mean” because that’s the only reasonable reply to “spec seems kind of weird and not well thought out either”. I’m asking OP to qualify there statement with something that could even start to be a conversation about various tradeoffs.

But really, i wish i hadn’t hit enter and i wish i could delete my comment because OP’s comment isn’t a good entry point to discussion anything.

Ok that's fair. But from my perspective as an outsider, the parent comment gave me a lot more info than yours did.

I don't see why you're focused on the comparison to Java's also-crappy-and-fragmented ecosystem.

When your parent comment said "it isn't as good as rust, etc.", that gave me a comparison point, because I'm familiar with how those other ones work. When you said "wat" to that, that told me nothing. Are you also familiar with those other ecosystems and find the Clojure one better? Or similar or equivalent? I have no clue whatsoever what you think.

You're right, the parent's comment on spec (which I have no idea what that is) and your rebuttal are equally bereft of content.

> Java has more build tools then Clojure

First of all, than*, not then.

Second... no, it doesn't. Gradle and Maven are the standards, and they both use the very well documented, incredibly successful, Maven Central repository. They are just two different implementations using the same, reliable, battle proven approach to dependencies. You need a library? Add it to your build file, with its version, and you're done.

And yes, whether you spell it "wat" or "what", both these words are a dumb way of participating into a discussion that do nothing but expose your ignorance of the topic you are trying to debate. Voice your concern, question, challenge, in plain English, or you'll just come out as childish, combative, and not educated enough to take part in the discussion.

Well with that logic it's the same for clojure, Lein and deps.edn they both use the very well documented, incredibly successful, Maven Central repository. They are just two different implementations using the same, reliable, battle proven approach to dependencies. You need a library? Add it to your build file, with its version, and you're done.