I've started learning Clojure about a year ago, couldn't say it was easy (the fault might be with me and not Clojure)
Clojure has a lot a faults (just look at some of the comments here) BUT and it's a BIG BUT for me...
Clojure is SUPER FUN :)
Over the years(15+), I've been coding in PHP (suck it haters), Go, Rust,Java,Python AngularJS+, Svelte and I can honestly say for me, nothing is more fun that coding in Clojure.
It's fun testing a function in "realtime" by just "eval" it on the spot.
I don't even code "in the REPL" I just use Calva and eval inline in VSCode
Maybe it's cause it's my new toy but it really does bring back the "joy of coding" I've been missing in the other languages.
*Learning Clojure became much more easier, once I told myself "It's Maps All The Way Down :D" Sure there are stuff like atoms that make it possible to code around the "immutability" but that is like using a cheat-code to go back to the old ways. Yes there are many times when mutable-data-structures are required, but while learning, don't grab for them as a first solution !
Anywhoo YMMV but once you get over the warts (many there are), much fun and insights awaits :)
Clojure user here since 2010(my earliest Clojure project on Github), and while I agree with the fun point, the iceberg wart for me at this point is the inelegance of the interface hierarchy and its structure behind the scenes.
Clojure's forward-facing interface (a hundred functions that operate on one data structure) ended up breaking down for me at some point and became 10 functions on 10 data structures and those data structures became AFn, APersistentSet, APersistentMap, APersistentVector, IFn, IPersistentSet, IPersistentMap, IPersistentVector, ITransientMap, ITransientVector, IndexedSeq, LazySeq, &c, &c, &c.
Maybe I started writing code wrong. Maybe I dived too deep into the internals, Maybe it was something else. But at the end, there wasn't one data structure, there were dozens and dozens each with justifiable differences, but even so, that's not what was advertised. It took a decade to reach that point and perhaps the vast majority of folks never will.
The sad part is that I know none of it is up for change without creating a Clojure2, and that highlights the problem. Why should changing the internals need to break backwards compatibility? There is one unspeakable reason: the illusion wasn't complete and they weren't really internals to begin with.
I've been programming with Clojure since 2010 and using it in production almost as long and I hardly ever care about the internal types so I'm really curious as to how you went down that "rabbit hole"? What sort of problems were you solving that necessitated delving into the implementation details behind the abstractions?
I've only needed to dig into that occasionally for a handful of specific situations (for example, in next.jdbc, where I create a hash map like abstraction over the (mutable) ResultSet object from Java -- to paper over some nasty interop issues).
Lets say that someone wants to improve on Clojure and make a better functional, immutable LISP! What should they start with ? Some things I miss is type support (for easy refactoring, auto documentation and performance), (small) native compilation, support for mobile platforms and their UI's, first-class web-assembly support, real structs and misc things like performant implementations for `first`, `last` and other clojure functions, light-weight concurrency like Go, data-flow analysis and pipelines, etc.
If you want an easy win, one thing Rich mentioned in a talk is - if he had to do Clojure all over again today - he would put transducers at the 'bottom'. For data transformation this makes the underlying collection type largely immaterial.
> real structs
I'm not even sure if value types are conducive towards immutable, persistent data structures. I'm certainly excited for project Valhalla but I'm not sure if Clojure, nor any Clojure-like JVM language, written in an idiomatic fashion, would really benefit from it.
> light-weight concurrency like Go
Project loom is already in preview mode. Lightweight concurrency is nearly here for any JVM language, and once it's fully released I will likely have zero reason to use core.async.
> type support
Static typing is A Thing you can choose to do, but I doubt you would get many daily Clojure users agreeing that it is "better". I think it's different and better in some circumstances but not necessarily others. It definitely feels trendy these days, sorta like how dynamic typing felt trendy 15 years ago.
Clojure users fighting against static typing are on the wrong side of history. They are fighting a side for all the wrong reasons, and they will lose.
There is a reason why all dynamically typed languages today are scrambling to add some form of static typing to their language, but never the other way around.
I understand the point you're making, and that you wanted to make it dramatically. I got a nice belly laugh thinking about my future grandchildren chastising me for forsaking static types:
"All your life's work for naught, Pepaw. If only you had been on the right side of history".
I do have a substantive disagreement with this statement: "There is a reason why all dynamically typed languages today are scrambling to add some form of static typing to their language, but never the other way around".
Languages like C#, Java, Scala, and Typescript have all adopted some degree of type inference. Their users wanted it for a long time. To begin with, none of these languages are anywhere near as strictly-typed as a language like Haskell. Clearly even developers on "the right side of history" don't want to maximize static type checking in all cases, so the issue is not as cut and dry as you make it sound.
Yeah and spec isn’t trying to press the real world into arbitrary categories? /s
The thing is, having some structure in terms of types or data shape constraints helps you model and understand your program. You can change them as your understand of your program changes or the need arises. You can be against static types because they force you to slow down and think about things before you can just get to coding, and it can be sometimes annoying to model your system using them (and sometimes not so useful), or other technical reasons like some type systems don’t allow you to model certain things, but to say it just leads to devs putting the real world into stupid, arbitrary categories when the real world (and your program!) primarily have data that can be trivially categorized because that’s what Rich Hickey said makes you sound like cargo-culting or parroting a point without diving deeper into the specific reasons and potential counterarguments.
However, to be fair, Clojure's response to types is `org.clojure/spec.alpha`, and if you give it a really good chance, you'll see that it brings a lot to the table.
It doesn't change that I believe strong typing (a Haskell or Idris/Agda-like flavour of it) to be the future, but what it did change was how I thought about type systems and how to write and test programs.
Yes on the type support not really attracting Clojure users. If you wanted to enable better refactoring, the thing to do would be to take more pages from other lisps and use the compiler to add more metadata to the runtime (in development mode at least) so that you can get better tracking of the language constructs.
I appreciate that it's a bit frustrating to have someone vaguely identify an area of concern and clarification and actionable suggestions are much more valuable.
There's two things. One is that the data structure ontology[1] is difficult to internalize because of its complexity. When I write Clojure code, I consider the capabilities of the data structures being passed to the function. I think we have some implicit understanding of this early on. You expect the type of such and such to support seq-ing or deref-ing or something else. You're thinking in terms of capabilities and that's good. But if you want to be exacting, at some point you'll have to be able to point to an interface or several in the diagram and say, "yes, I really do expect something that conforms to IPersistentVector or IMeta or something else. In order to preserve backwards compatibility, this is more confusing than it needs to be. Moreover, at that point, you're not in Clojure-land anymore.
My second point is that at this step in the narrative, you're reading Java code and the illusion of 100 functions is broken. I ask myself, "Why, aren't Clojure data structures implemented in terms of protocols?" There's a perfectly good way of abstracting functionality that seems good enough for users, but not good enough for standard library implementers? It feels like the answer is, "We don't want to break Clojure1.x and we don't want to deal with the Python3 problem." Which is fair, admirable even, but it still leaves me left wanting. I think there's a simpler, more consistent Clojure hiding in there, waiting to be let out.
Apologies for the awkward way this reads; I simply wanted to get it out before lunch time.
I don't want this to sound hostile because I have a similar love/hate relationship to Clojure, I truly don't understand what you're arguing. Or, it seems like your issue is with lack of static types rather than an issue with the implementation? I've written a lot of Clojure and have an okay to pretty good understanding of the implementation, but it's just hardly ever relevant to the code I write, so I still don't totally understand what you're getting at exactly. Like, what's the actual problem here? Just that there's more Java "intrinsics" than pure Clojure code? I guess I do agree that Clojure is really just Java, and it would do better for people to think about it just as (really nice) syntax sugar over Java, but I guess I don't see that as a problem, but rather the absolute biggest strength of Clojrue.
I think he is arguing for more protocols than simply sequence. That is a sequence can be attributed fine-granular capabilities represented by protocols and that allows generic functions to be tailored towards those capabilities for improved performance.
Are you really suggesting type support? I don't mind types but I think spec is more in the spirit of Clojure. Have you taken a look at it? https://clojure.org/about/spec
spec is mostly useless for tools and IDE's and static analysis. Spec necessitates running your program with an arbitrary runtime complexity. Type checking is done before your program runs. The idea is to remove the need for runtime checks as much as possible.
But clojure is not meant to be developed statically!
It is supposed to be running while developing it. You don't need static analysis if the programm is running and inspectable.
Right ! But you still had fun the last decade correct /s ? :P
My "real response" to is, I haven't seen this complaint(concern) to this extend that you describe in the wild or in my own life. I've definitely not coded in Clojure long enough to have seen any of that like you have, thus far my experience has been good with 'just' using maps.
I do feel my next "step-up" would be to incorporate something like SPEC or Mali to "define/check" the fields/structure of said maps.
An interesting complaint. What problem domain(s) do you work in? It sounds like you've found something that Clojure isn't a great fit for.
The mix of interfaces would be a huge pain if you needed their specific behaviours, but in my experience they've never mattered and lings like LazySeq just act as performance optimisations.
Do you have some os projects out there to share where you had to do that? Makes me curious, I think the only time I've ended up making a specific data structure in clojure was for a library for perf reasons. In application code basically never.
Specifically it was using GraalVM to interop Clojure on one side with React running on GraalJS on the other. It definitely pushed the limits of what is or even ought to be possible, but at the same time highlighted those very issues. I'll be the first to admit that this is an extreme edge case, but that doesn't take away from the fact that the case is still there.
Same here, started using Clojure pretty much exclusively about a year ago and never looked back. It was rough to get to that comfortable state where I am now, having to find out how Clojure CLI tools work, what Java Classpaths are, how to properly set up my editor (Neovim), how to make sense of Clojure internals like “seq” and when to use which collection type (and when not to care), etc.
But it has also been a fun and liberating experience, after I went down the static type route for some years. I feel like Clojure just doesn’t get in my way and doesn’t force me to follow a certain style or paradigm; it provides many different choices (“providing more” instead of “taking away”) and treats you like an adult who can make responsible decisions on their own.
Although I missed the linguistic art of domain modelling through type definitions, spec gave me some of that back and even provides stuff like generative testing, which is pretty awesome. Of course, it is not meant as a replacement for static typing and has a different philosophy (see Rich Hickeys talks about it), but for me it is the best of both worlds.
The dynamic development style is something that I experienced before through learning Lisp/Scheme and it is also one of the thing that I enjoy most about Clojure. Coding “experimental”, building functions from individual pieces that I iteratively design using the REPL as my assistant (which actually means - as parent wrote - communicating with it from the editor itself, which we mostly do), feels so natural and I would miss it now that I am so used to it.
> treats you like an adult who can make responsible decisions on their own
I have to disagree with this characterization. It also seems a bit patronizing.
Having more choice can be good in some cases, and having fewer choices can be good in other cases. One isn't treating you less like an adult than the other.
> doesn’t force me to follow a certain style or paradigm; it provides many different choices (“providing more” instead of “taking away”) and treats you like an adult who can make responsible decisions on their own.
This is really not correct. Certain choices Hickey made, prevent you from coding as you like. E.g., Hickey doesn't allow strong reader macros or inheritance in Clojure.
Stage5: All other languages and their syntax now looks ugly and wrong. I'm mean for heavens sake put the parens on the outside ! We are not heathens.
Lol only half joking. It really did surprise me how can something I hated some much in the beginning of learning Clojure (all the parens) has now become one of the things I most adore about the language (all the parens)
It's really hard for me to see any justifications for a syntax that's not based around s-expressions. The benefits of being able to trivially transform any code as data and have powerful structural editing facilities outweigh any perceived downsides in my experience. I also find that s-exps act as a visual diagramming tool making your code more scannable because you can visually see the relationships between statements by looking at the nesting.
> I also find that s-exps act as a visual diagramming tool making your code more scannable because you can visually see the relationships between statements by looking at the nesting.
I think this is where people disagree regarding syntax. I love Clojure and agree with all the benefits of s-exps, but find it basically impossible to scan and very tedious to read. While it tends to be easy to refactor do to the REPL, at least for me it's on the wrong side of readability vs expressivity.
When I look at Clojure code, it just looks like noise, all the forms look the same. For example, a let and a doseq at first glance might look almost identical in terms of their "shape." In something like Java, a for-loop "looks" like a for-loop, there's nothing else in the language that looks quite like it. If I see a series of chained lambdas, there's an extremely high chance I'm looking at a stream, etc. The syntax helps me here by adding more obvious visual markers.
The argument I'd make in favor of Clojure and s-epxs is that, when you write good functional code that composes lots of small functions, it can literally read just like English. If foo and bar do baz else qux. But bad Clojure code doesn't have this property to me, it just looks like a lot of noise.
Stupid question, but do you have something like "rainbow-brackets" active in youre code-editor ? I found it really help me.
The more I read LISP/Clojure code the easier it becomes.
I agree that it's possible to write dense code that's hard to read with Clojure, but that is true in any language. I've seen plenty of Java codebases that I couldn't make heads or tails of. As you point out, well written Clojure often reads like plain English. So, in my view it's just a matter of making the effort to write readable code on the part of the developer.
I've been working with Clojure for the past decade, and it's still the most enjoyable development experience I've had. Once you experience using an interactive workflow where you can see the results from the code you're writing live, it's really hard to go back.
I physically suffer when I go back to systems like django, where there's no principled underpinnings, no 'protocol' and only a thin way to interact with the system live.
Can you recommend a good YouTube video that demonstrates this? I'm used to writing code incrementally in Python via the IPython REPL. I've also used Twisted's Manhole to get a REPL inside an already-running Python server process. But I believe you're referring to something substantially richer than that.
For me, it's a combination of having a REPL and having a good interactive interface between the editor and the REPL. Typing forms into and evaluating them in the REPL is one thing, but typing them into your editor file and evaluating them from there in the REPL is much more useful. (I can't fairly say one way or the other whether that's possible/common in IPython.)
There's a lot of idiosyncratic advice in those Halloway talks, and people shouldn't blindly adopt it wholesale without evaluating if it's right for them.
Learning to evaluate forms directly from the editor is great, but _never_ typing in the REPL forces you to clutter up a file with evaluations you may not want to save, and jump around a lot between your comment blocks and code.
You can do interactive development in Python using emacs and the “elpy” package. I’ve only used it for small scripts, so I can’t say how it compare to Lisps.
it's easy to run a lisp (clisp on linux) or scheme (guile on linux) REPL and then paste in example code and see what it does, there's lots of examples out there. Then I'd say try playing with "call with current continuation" if you want to see mind-bending elegance (or let's say it's an elegant implementation of something you'll never be sure you understand)
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.
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.
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)
(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.
=> 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).
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).
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.
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.
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 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.
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.
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.
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.
> 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.
> 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.
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.
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.
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.
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.
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.
To toot my own horn: The oldest still-active compiler-based language on the Java platform, besides Java, is as far as I know Kawa (https://gnu.org/software/kawa). It started Summer 1996, and is still actively maintained. (Not as active as it used to be, to be true.) Like Clojure, it is a Scheme-like language, but (unlike Clojure) it is very compatible with "regular" Scheme (R7RS), though of couurse with lots of extensions. With a little bit of care (including optional type-specifiers), it is easy to write code that is as fast as Java code.
I have the feeling that after Nubank bought Cognitech, Clojure has gone to stagnation. I mean, i feel that promoting and improving Clojure is no longer a priority. And another think that stinks me everytime Cognitech talks about Clojure they have to bring to the table Datomic. They tried to push Datomic on my company long time ago when they do some consultancy job at my company. You can skip all the talk, because is all about how good Clojure is and that is. No any eta about future features, improvements and fix problems of current Clojure users.
Lot of open source contributors have leave the community, because there is any plan on Clojure. I guess that Rick Hickey is happy with Clojure as it is now, and this is the way is going to stay. And still no Java 8 support for CompletableFuture, lambdas interface, Stream api, java.time, no pattern matching, java records, this group feels like a group of old programmers stuck at Java 6 that cannot move forward.
All the things you are mentioning would be nice to haves but frankly nothing prevents us to use these today already, either directly or via a bit of wrapping.
Communication "style" and slow pace is frustrating sometimes but that's a small price to pay for all the positive facets of the language/community. I used to be more critical of these but I don't care anymore, the community is wonderful, the language is very usable and thriving, that's what matters ultimately.
About Datomic, I still don't get why there's no push to open-source the on-prem version, especially since nubank acquisition.
I dug onto the internals a few times, contributed to some of the alternatives, read/viewed pretty much everything about it and used it for fun in toy projects, and that thing is just so versatile it makes me angry it's not more accessible and as a result not more popular. It has an incredible untapped potential.
Every conj I am holding my breath hoping for a "one more thing" announcement where they'd do just that.
The "alternatives" do things either quite differently on too many aspects or lack traction.
Which of the datalog alternatives do you think is closest? And how much of an improvement do you think a datalog db is over boring Postgres or SQLite? I’ve been weighing what db to adopt.
datalog is just the query language. For me there's no such thing as a datalog db, there are various db that uses some derivative of datalog for querying but that's it. To give you an idea, datomic has other ways to query your data than with datalog alone.
The "closest" to datomic is datahike right now.
Crux/datahike/datascript/asami all use datalog in some way or another but they cover different use cases.
It seems to me that these things are a big deal if you're doing a lot of JVM interop and not so much if you're primarily using Clojure-native libraries.
Clojure has futures, lambdas, streams, a community-maintained java.time library, core.match, and Clojure records. I'm sure there would be benefits to adopting the newer JVM-native equivalents, but I'm not as sure they're worth the costs.
I thought you might be trolling. But then when I looked at the Clojure repo on Github https://github.com/clojure/clojure the last commit was 2 months back. There is some merit in your arguments.
Clojure evolves slowly and deliberately -- it gets about one release a year (with various prereleases) so "2 months" between commits isn't a big deal. I consider it a "feature" because it means Clojure is extremely stable -- which is great for us at work given that we've used it in production for a dozen years at this point and have been able to run alpha builds in production safely during nearly all of that time.
Lisp has ruined me forever.
I'm not sure how I can go back to other languages.
I basically started out with Allegro Common Lisp on Sparc's, went to Java because it was the shiny new thing, and then in 2017 finally took the plunge and have been addicted to Clojure ever since.
Don't get me wrong: the learning curve can be steep, but the steepness - at least in my case - came from un-learning all the object-oriented stuff.
Once you understand the value of values and the simplicity it brings, it becomes such a joy to code in Clojure.
For the people on here disgusted by the JVM ecosystem: There are many alternatives for Clojure.
There's Clojure for .NET, ClojureScript in at least two flavors that compile down to JavaScript, there's a fast interpreter called babashka for scripting, there's Clojerl for the Erlang VM, there's jank - which adds gradual typing on a C++ runtime.
How is Clojure for dotNet? I haven't touched Clojure in general for years (honestly I think it's been at least a decade) but I remember a lot of libraries making calls expecting the Java standard library so seems like things would have broken, but maybe it is gotten a lot better in recent years.
The Clojure development process is notoriously closed to outsiders.
Even in the early years, some people contributed time and code, and weren’t thanked. I would view attempts to contribute as very high risk for newcomers.
I personally maintained a fork for a couple years just for an unmerged patch I needed, rather than try to get the core team to merge it.
You don't have to be using a lisp to benefit from the different way it makes you think about things. In most languages it's at least somewhat possible to use some of its good stuff.
I love Clojure (and have touted such in past comments), but it suffers from a glaring problem: every library is half done and/or abandoned. What happens is you end up modifying an existing library to fit your particular problem space. I needed a web framework. None of them just did things in a "simple way". I ended up branching an existing one and have altered it (very heavily) to fit what I need. It's now my go-to for all new projects.
The issue with us Lispers is that we love the language more than we do the tooling. Tooling be damned! So we all end up re-inventing the wheel for the 1000th time. We will never gain wide spread adoption, as such.
With Ruby, I have Rails. With Python: Django. With Clojure...well good luck. Every framework does one thing beautifully correct and about 10 things wrong. But hey, it's up to you to modify the framework! Because that's the Lisp way of doing things!
Maybe I should publish my bespoke framework one of these days...
I think every clojure dev that sticks long enough and feels the need for a framework ends up publishing one. The reason none of them stands out much imo is that library composition is pretty easy to do and the building blocks libraries out there do their thing pretty well. So with a bit of experience even on a new project it feels much easier to adopt your known set of building blocks, maybe throw a few new ones into the mix or replace one and you are good to go. At the end of the day it doesn't take more time than learning a framework yet it gives you a lot more flexibility.
I don't think it's an "early ecosystem" though... I was playing with Clojure before I had ever heard of Go, Elixir, Julia, Rust, or Typescript. All of these seem like mature ecosystems now. I really like Clojure as a language, but if it's still an "early ecosystem" at this point, that seems like a problem to me.
True, although I don't know which library the op is talking about, because all the libraries I use are alive and well, some get very rare updated simply because they are "done"
Also, it has a low frequency of bugfix commits (which may or may not be related to bugs also). (but it might be the dev experience, I have not adjusted for it).
In addition, the JVM gives it great performance, and it has actual CPU multithreading (while Python or other scripting languages have a GIL). This makes it relevant in today's environment where Moore's law continues through number of cores.
Therefore I don't think Clojure will die too soon.
I was writing a longer comment and deleted everything after it started to look like bragging.
All I'll say is that I'm extremely grateful to Clojure for the life I've been able to live and what it's allowed me to accomplish. This is truly a language for beating the averages.
The ultimate failing of Clojure as a tool is its inefficiency measured in $ per feature.
The language is not very approachable for junior programmers. You can grab a Python programmer and graft them on a Java or TS project and the same week they will deliver features and bugfixes. Reading other's people code in Clojure is much harder especially when coming from imperative world. The language is terse. The documentation is terse and lacks examples. Library documentation is insufficient or non-existent. You have to read other people's code. It is a really demanding activity aggravated by lack of static typing. You cannot just hover over a map in your IDE and see its structure. The value may have come through several transformations that add, transform and remove fields. To understand what arguments a function expects you have to decipher destructuring bindings which in real code can be ingenious, i.e. unreadable. And hold on to your butts when a clever teammate invents a flow of control macro and uses it throughout.
These and other reasons make the pool of candidates small, candidates "over-qualified" and expensive. If your expert quits, your whole endeavor is screwed.
I helped to rewrite a Clojure project that stalled due to lack of affordable talent. It's been chugging along since then at a considerable pace. The new language is very popular and much less affected by the mentioned problems.
There is a mention of Penpot in this thread. Mark my words, when it comes a time to scale, they will rewrite the whole thing in a popular statically typed language.
I have onboarded developers coming directly from university and it went fine?
In fact, at my old job, the entire team learned Clojure on the job. I was the only hire who had learned it prior to being hired. Our codebase was fine and very maintainable.
The other teams were similarly new to Clojure. Most people spent a few months doing increasingly more advanced things until they were comfortable with the language and the codebase.
The main issue for complete beginners is learning to grapple with the different development setup compared to what they might be used to, i.e. interactive development on a live system using an editor-integrated REPL. We basically set everyone up with IntelliJ and parinfer. Paredit was optional, as was alternative editors such as emacs.
IntelliJ + parinfer is a similar development experience to Python. The only part that is foreign in that case is the interactive development of a live system which the new devs quickly come around to seeing the benefits of. It helps having seniors who point out the new concepts.
I remember writing about first official releases of Clojure around 2010 when I was an editor at heise online Poland. At the same time I was coding libraries in Ruby and one thing was drawing my attention: lazy enumerators.
During works on I18n library with pattern interpolation in Ruby I had chosen to use lazily executed methods which could be stacked with the dot operator, giving nice processing pipelines. Around that time I was reading about Clojure and it hit me how much easier would this be in this language, with function composition and sequences, not mentioning the Delays or Futures.
I think Clojure was the first language I decided to learn before conding anything serious. It took me like 2 years to really give it a try, and leave Ruby world, so in 2013 I was making notes explaining how the basics work, and in 2014 started writing a tutorial in Polish called "Poczytaj mi Clojure" (which could be freely translated as "README Clojure" (README meaning both "Clojure, read to me" and "read me [some] Clojure". Through all 2015, during my sabatical, while sitting in a cafeteria almost every day, I coverd built-in special forms, functions, type systems, collections, sequences, macros and more, publishing it online.
In 2016 I started sharing first programs on Github. So I probably need about 2 more years to become advanced, according to "Teach Yourself Programming in Ten Years" – https://www.norvig.com/21-days.html
I remember trying Hydrox for documentation and so called literate programming approach, playing with function arguments and building macros changing positional args into named ones, using core.async and multimethods to build network bots, learning macros and protocols. I've made some free software libraries through the years, and I think I finally am able to build more complex systems.
Since I occasionally have a tendency to go into details too much, or to (re-)write things from scratch, I found Clojure to be the first language giving me enough power to finish those things in less than couple of weeks (1-2 months tops), and go back to a main project to continue. In other lanugages I tried for years I was kind of sinking into sub-projects (which needed to be taken care of) and hadn't enough energy to go back and resume works on more generic, systemic level.
One of the things that stands out to me in this video is seeing devs who are much older than the average dev I encounter, who have continued to grow deeper in their skills and form deeper insights, sharing some of those insights. It's something I don't get to see very often and it's encouraging
Clojure is the language that made me understand why Lisp matters: We can express ideas and instructions in a programming language with just a very basic set of tools. And we can use these tools to craft powerful and elegant abstractions. In addition, Clojure emphasizes immutable data structures, which, after some learning effort, made me write code that is much easier to reason about.
Clojure fans seem quite taken to hyperbole. If the language is such a "joy", "ultra-productive" etc I would have expected after such a long period of existence some major open source project to be showing off what the language attributes allow you to do.
Happy to stand corrected if there is such a slam-dunk showcase that I've missed, I am actually interested to dig into clojure, if nothing else as a way to deepen my understanding of functional programming.
Thanks. I don't know to what extend its "better-because-of-clojure" but I also found overtone https://github.com/overtone/overtone which should be good fun (though the underlying synthesizer is supercollider/C++).
I guess it's impossible to know what you've seen before and dismissed as not having sufficient features/attributes/popularity. Or what types of project would cause you to dig in but here are a few innovative projects using clojure that represent a fairly diverse set of interests.
Clojure is associated with the JVM platform, Java and Scala and Kotlin end user open source apps are also underrepresented relative to dev population. There are lots of OS Clojure projects but they are mostly tooling, libs, databases, etc. There are a lot of business and data stuff most of which isn't open source.
Come to think of it are there many TypeScript open source apps either?
There is something about the language and/or community that leads folks to build their own X instead of collaborating on a common open-source version of X.
Perhaps the lisp learning curve is high enough to dissuade those who want to contribute to a project but don't yet know Clojure?
I have a pet theory that it's because it's too easy to write code in. I think there needs to be just enough pain with a language or library that it becomes worthwhile to allow someone else to write X and just deal with it being imperfect if it still solves your problem.
Excellent and very productive language. Personally, I did 15 years of straight Java (before that C and C++), then 5 years of Clojure. For me, the most telling dynamic is when I recently returned to a Java project and realized how much better Clojure is - even for large projects ;)
The lack of static typing, which is so often used as an argument against Clojure, is actually what makes Clojure better. The Java projects become a morass of types scattered into a myriad of packages; no matter how experienced you are with Java. Clojure's dynamic underpinnings and its paradigm of hundreds of functions for a few data structures allows for very lean designs that are easier to wrap your head around and keep in your mental models.
The other features are just gravy: immutability built in, REPL interactivity, Transducers, Protocols etc.
While I like Clojure as a language - I have never seen a language that engendered such hatred in PMs, EMs, sales, etc. I don't know that it survives long term (in industry, it'll survive for a long time as a hobby language.)
This is a reasonable comment, even as a Clojure fan, I think this fits with how the community thinks of itself to some extent. I can't provide a specific citation but I know there is at least one talk (maybe Simple Made Easy but don't quote me lol) where Rich Hickey talks about the fact that one reason "easy" tools -- which in his view provide fast uptake but more problems down the line due to being "complected" in how they deal with various concerns of the underlying problem -- are so popular is that people who manage programmers (like some of the positions you mentioned) want to be able to easily put butts in chairs, to readily replace engineers like they are cogs in a machine, so they are very willing to make this trade of short term ease for long term pain because less training is required to get to a near term deliverable.
It's also clear (see Hickey talk Effective Programs, 10 Years of Clojure, where he surveys the room) that Clojure programmers tend to be senior (real senior not 5 years experience "senior") and a little grumpy about the state of the art, opinionated, and don't want to be cogs. This may also explain the allergic reaction among people who manage programmers/engineers. Managers as a rule (especially at certain orgs) tend to want people who are more compliant and less free thinking and likely to push back.
Anyway I think you make a good point except I disagree about its survival long term. I think there's something to be said about the value of being more popular among older more seasoned programmers vs PMs. How many PMs in the 90s foresaw the rise of Linux (vs proprietary unix and Windows), how many go overboard on "agile", how many were into ruby on rails before it picked up among programmers, etc. Managers tend to be a lagging indicator (speaking very broadly).
My impression is that Clojure (and Lisps in general) are about maximizing the productivity of an individual where other languages try to maximize the productivity of an entire team. There are trade-offs to be made in either direction and much of what affects the feel of languages on either side of that comes from the choices they make around those trade-offs.
Clojure is really good at self-selecting for people that really care about innovation in programming languages. The other jobs in a tech company care about innovation in their domain. They rarely align.
So for every weird, cool innovation you see, the productivity is immediately lost because people equally care about how you deal with state on app startup, or routing, or database interaction. And since none of those other things move the needle much in how you compete in your business domain, competitors who are not concerned wind up eclipsing you. While you're arguing integrant vs. mount, people are just running dotnet new and bikeshedding over things closer to the business domain.
PMs/EMs/QAs/etc. may not have technical knowledge, but they smell something off about programmers arguing about what library to use for routing. Why didn't people complain about this at their past jobs? Surely if it was important, it would have come up. They just perceive you as having hired a bunch of senior people who are incredibly slow relative to their past companies. It really breeds resentment.
I have seen clojure-first companies creatively hive off parts of their engineering org to have a separate org that is allowed to use a different stack. They're not super transparent about it to avoid a big exodus of people, but I've personally seen it happen more than once.
> Clojure is really good at self-selecting for people that really care about innovation in programming languages. The other jobs in a tech company care about innovation in their domain. They rarely align.
To me it seems people typically like Clojure because of its simplicity, stability and productivity with the REPL. There are innovations in Clojure (its data structures, transducers...) but overall it is a pretty conservative and minimal language with laser focus on pragmatism.
Also in terms of slowness it seems the opposite is the case from my limited knowledge. OS authors and contributors seem to be extremely productive and creative. There are some notable Clojure shops (see: OP) that have been growing at an incredible pace.
Maybe I see this differently because I come from a [insert two very popular languages] background where everything breaks every couple of months and the culture is severely fad driven. To me Clojure is refreshing, calming and more powerful on top.
Sometimes with time SW based companies seem to migrate towards "easy to hire maintenance devs" style stuff once the products mature and slow down. It seems to happen to Ruby and Python projects too. But you might not have gotten it built in the first place if you started that way.
(Though I don't think Clojure is really "innovation in programming languages" stuff, it's a rather straightforward Lispy language and has changed much less than eg Java, C#, JS in the last 15 years, and is simpler too)
I like Clojure, but unfortunately for web development, there isn't a great stack and for data science/ML/AI (which is a great fit for clojure), python dominates the market. What is the niche for Clojure, or why should keep using it in 2023?
P.S: I like it a lot, but either I do golang or ror for web dev, or python/pytorch for data sciences/AI stuff, C# for game development. I don't think i can get more productive with Clojure in any scenario, that I'm exposed too, what I'm missing?
>Frontend development is simply awesome in ClojureScript
Is that still tied to that google closure abomination ? I've not been in CLJ ecosystem for >5 years now but last time I used CLJS I remember they went all in on google Closure and it was a major PITA to use the rest of JS ecosystem because of it.
Every time I use React I think how it would be great to use Clojure for that, but last time I tried the tooling overhead just killed it for me.
> Is that still tied to that google closure abomination ? I've not been in CLJ ecosystem for >5 years now but last time I used CLJS I remember they went all in on google Closure and it was a major PITA to use the rest of JS ecosystem because of it.
This has been solved now--there's a blessed CLJS solution[1][2] as well as a third-party compiler with NPM support[3] which many prefer.
Yes, ClojureScript is definitely on my learning list, we however decided to avoid one more transpilation tool, and accepted the fact that we have to write typescript.
> Give me Reagent + Shadow CLJS over plain React (or React Native) any day.
So that is for heavy js based applications, not for something light like rails + stimulus.js, right?
The cljs stack I hear about a lot (and use) is ShadowCLJS with reagent (https://reagent-project.github.io/) and re-frame (https://day8.github.io/re-frame/). ShadowCLJS is more of a build tool, but is really well documented and easy to use. Reagent is basically react but a simpler API, and re-frame is a layer on top of that kinda like redux. It's overkill for some apps but I find it's actually super easy to work with and not as much complexity as I thought.
For backend there is luminus (https://luminusweb.com/) or Kit (https://kit-clj.github.io/). They are basically project templates that wire together a ton of popular solutions for various things - database access, migrations, security, html templating, etc. Also includes frontend frameworks like re-frame if you want.
edit: forgot to mention fulcro (https://fulcro.fulcrologic.com/) which is an interesting full stack solution. I haven't used it though so can't comment, but it sure seems documented well!
Thank you. What about leiningen vs boot vs deps.edn? Did any of this come ahead as a winner in the recent years? It's been a while since I've looked into this.
The choice is between Leiningen and deps.edn AKA the official Clojure CLI. Personally, I switched to deps.edn back in 2020 and haven't looked back. I like that it's simpler/does less out of the box and I also use the Git dependency feature all the time (you can reference a git sha as a dependency, not just Maven coordinates).
Something like 90% of it is just listing dependencies (the same packages as it's all just Maven anyway) and for the remaining 10% of configuration there's plenty of resources available for both. They can also interop to some extent.
Other ecosystems have it much worse than Clojure in this regard.
Boot died, sadly. There was some talk of a Boot 3.0 but it never materialized. I think deps.edn took all the steam out of it. Duplicated a lot of functionality and none of the esoteric-ness.
If you want a complete, integrated "stack" that stitches some of the most common libraries together in a uniform structure you might have a look at kit:
If you're coming to Kit from Luminus make sure you understand Kit's use of Integrant which involves passing `query-fn` down from the route to every function which accesses SQL. It was a deal-breaker for me.
I recently have been building a internal web app for managing customers and am super happy with shadow-cljs (develop/build) + reagent (view) + re-frame (state/events) + garden (css).
I especially like how easy shadow-cljs makes it to utilize both clojars and npm pacakges. I have been using JS/TS for a long time and feel like any webdev without npm packages would not be worth my time, especially for projects at work.
Re-frame has a very robust api for dealing with events, side effects, and state. Once I got used to the concepts in the documentation, very good docs but a little goofy, It has quickly become the most efficient and fun tool I have ever used to prototype complex frontend interfaces :)
Glad to be using cljs at work, but I'm not sure I would use this stack for non-internal tooling though.
> I like Clojure, but unfortunately for web development, there isn't a great stack
What do you feel is missing? Certainly there are options (something as frameworky as Fulcro, something as barebones as a React wrapper like Reagent), so there must be some itch you can't scratch?
Yeah, it's too heavy for some things, but it's situationally very awesome. Fulcro takes the fantasy of "reusable components" very seriously, more seriously than any other frontend framework I'm aware of, and designs from the ground up to allow for that. Be sure to go to #fulcro in the Clojurians slack with any questions/comments--it's a very friendly community in there.
I tried getting into Clojure (I have an undergrad Lisp background). I just couldn't become productive, and the tooling seems fragmented. Been moving many of my workloads from Go to Rust.
Clojure changed my life. It made me realize how fun it is to code and switch back to being a programmer. This makes me a much happier person and thus a better husband and father. Never looking back. The Clojure Way just fits me perfectly.
He certainly looks and sounds older. Rich seemed to ride the wave of Clojure's early success with a succession of brilliant presentations. Then .... nothing.
Obligatory shout out to Babashka [0] which is interpreted Clojure. You just download a simple binary and you can get going. Widely used for quick-running scripts, with a lot of batteries included.
I have been using Clojure for a solo project for a while now and find it shockingly productive. I initially planned to limit how much of the language "surface area" I explored to keep focused on solving the problem but was able to productively use much more of Clojure than I expected:
-core.async for concurrency that led me speed up some tasks from ~1000ms to ~40ms (well, core.async got me to about 100ms, or closer to 5x speedup on other tasks, and then other optimizations got me the rest of the way). Also using refs for some very limited data sharing across go threads (apparently actually using refs and dosync, aka mvcc data structures, is rare).
-spec for parsing a dsl and validating input
-macros for a select few syntax and core async optimizations (avoiding unnecessary go block usage)
-multimethods for extensibility
-a fair amount of Java interop to use best in class libraries
-transients to help optimize performance in some places
-even transducers a few places (maybe 1 percent of my code if that, but still)
-edn for dsl files
I sense that where people have more friction is trying to work with other people, in teams and organizations. In the right organization this can clearly work, dedicated Clojure shops, but there can be resistance in other organizations, and it has to be "smuggled in", and people worry about the ability to hire (especially for orgs that tend not to train heavily - nubank clearly trains a lot of programmers on Clojure but many shops don't want to do this level of training). And then people will avoid using certain advanced features like macros or even some functional conventions (recursion, reduce and map etc instead of vanilla loops) because they worry it will make it harder to onboard people without Lisp or FP experience.
I honestly think this is Clojure's big challenge, entrenched expectations and conventions in the industry and a desire to pull programmers off the shelf. In this talk (the one we're all commenting on) Rich and others point at the productivity and fun of REPL driven development -- finding some way to really grow that and somehow take it to the next level -- as a possible solution, to offer yet another carrot to encourage people to make the leap to Clojure. It sounds like a good idea but I have no idea what that looks like exactly.
I honestly think the answer to getting more Clojure penetration may be the passage of time, as happened with python. I know nubank/cognitect has been funding some open source work with small grants on top of Alex and Rich's work (and Stuart's?). This kind of basic grunt work can pay off long term. But it's not a sexy answer and if it doesn't work you're at a dead end.
I have also been using CLJS/Clojure for a solo project. I debated between CL and Clojure for the backend, but CL’s package ecosystem is too weak to be justified. What other languages did you consider for your stack and what did you think were the tradeoffs?
This is sort of a next generation successor to a personal project written years ago in Perl (evolving from a form fit tool to something much more general and flexible). I originally was going to do it in Ruby but after learning Clojure and doing a bunch of book exercises (toy coding tasks) I thought Clojure would be a better fit. (When I learned Python I considered that but it did not seem any better of a fit than Ruby and since my use case was web oriented it seemed like Rails could eventually be useful.) It remains a personal project but one I (unlike the predecessor) think is good enough to eventually open source.
I think my motivators to do in Clojure were edn (dsl files are a key part of what I'm building), the concurrency story being strong, Java interop for libraries (Ruby wasn't necessarily missing anything but without the Java libs Clojure would not be an option), and a general belief that Clojure's overall approach (data oriented, LISP, functional) would prove generally more productive (I believe this is true but have no evidence since I never tried building it in another language). Spec has been an unexpected benefit as it saved me from writing a lot of validation code and because it can be used as a parser for the DSL (it does most of the heavy lifting).
How has your experience with CLJS? I have been using vanilla CLJ but curious to some day try CLJS.
CLJS has been surprisingly good. re-frame really simplified a lot of the development. There are some oddities with trying to work out whether something is a JS problem or a CLJS problem, but for a solo project, it has been pretty good. CLJ/S seems to me a good spot between practicality and idealism (CL being the latter for some things). It's the most practical Lisp atm, and it can't get better than that.
I see some comments in here with the sentiment, "Clojure—What am I missing?" Here's an attempt to answer that question. I am just a hobbyist and enthusiast programmer with no formal background in programming or CS, and no real experience using Clojure (or any other language, for that matter) "in anger" in a serious production environment. These are just some nobody's two cents, take or leave them for whatever they're worth to you.
From what I understand, there are some applications and domains that the JVM is highly optimized for—especially long-lived, highly concurrent server processes or stream processing applications where microseconds matter in order to keep up with real-time. The fly in the ointment (for some) is that Java itself is Object-oriented, compiled, statically typed, and uses mutable objects and data structures by default. Not everybody loves those design choices. And even of those who do, not everybody finds Java ergonomic (hence Scala and Kotlin, among others).
So, much like Scala and Kotlin do, vanilla Clojure just gives access to the JVM (and its highly mature library ecosystem) in different trappings. It's a functional (rather than object-oriented), REPL-driven (rather than compiler-first), dynamically-typed, and immutable data-oriented language, with Lisp's minimal syntax and structural editing experience. Plus, the designers have invested a lot of time and their cumulative decades of programming experience into making a really solid standard library that comes with a lot of smart affordances out of the box. That combo really works for some people.
Then, folks who appreciated the conveniences and good design in vanilla Clojure started looking for that dev experience in non-JVM contexts, which is how you get ClojureScript, ClojureDart, ClojErl, Babashka, Scittle, etc. And then, other Clojure-inspired languages pop up that don't purely adhere to Clojure conventions, but co-opt significant portions of its well-designed syntax and standard library—languages like Janet (compiles to C) and Fennel (compiles to LUA) and Hy (compiles to Python).
And then, script kiddies like me come along and discover, Hey Wow, I can learn one syntax and suddenly be +80% dangerous in a dozen different environments, without having to start learning a new language from scratch in each one—from the JVM to the browser to mobile apps to native executables, I've got SOMETHING to start building on because I already know how to think in Clojure.
At this point, "Clojure" is not just "a language," it's a category of languages—just like Scheme is "a Lisp," Janet is "a Clojure." I don't think that happens with uninteresting, poorly-designed, or irrelevant languages. Clojure scratches a specific itch, and not everybody has that itch to scratch. But whether Clojure is your cup of tea or not (and obviously for many people in this comment section, it's not), I don't think anyone can debate that it's a significant project that justifies its own existence. That doesn't obligate anybody to use it or even look at it twice, but I think it self-refutes a lot of the negativity that gets thrown its way.
Nice to see, but I'm still sad Clojure is a hosted JVM language. I find it unwieldy for a lot of things where Go, Python and Chicken Scheme shine, and wish someone got ClojureScript to run natively in things like Bun or Deno so I could have one fast, small runtime.
a little bit of an offtopic question, but given that the video has a sign language interpreter, is there a specific vocabulary for programming given how many unique terms there are, and is it harer to understand for people who rely on it than normal conversation?
In Clojure ORMs aren't really that popular considering most Clojurists just use the built in datatypes (e.g. maps) to represent data. There are no classes as such (unless you use Java interop). If you need a query builder the honeysql library is really nice.
There's https://www.hugsql.org/ for example. It takes maps for inserts and updates, and returns maps for selects. Other database access libraries over the years have typically shared that characteristic.
If you want to convert between maps that map directly to tables and some other structure, manipulating maps in Clojure is easy.
Expanding on this a bit, Clojure, as a language, is fundamentally against the idea of ORMs in its design.
Objects / classes are not so much the problem, per se—it's specifically that ORMs fundamentally involve scattering uncoordinated mutable state throughout the application. A foundational thesis of Clojure is that mutation is a tremendous source of bugs, and should be avoided / thoughtfully limited.
Once you let the unmanaged mutation genie out of the bottle, it's almost impossible to put back in.
More concretely, I used to work extensively with Rails; I loved ActiveRecord (ORM) when I first started out—it makes basic things so easy.
Later I worked on a large Rails app supporting millions of users...we used ActiveRecord extensively, and had a very talented team. ActiveRecord worked fine most of the time, but I have bad memories of spending hours or even days tracking down user-reported bugs.
I'd try to figure out how to recreate the user's state locally, even cloning pieces of the production database to work with their exact DB data, but whatever state the program had gotten into was a large graph of (all!) mutable objects. How was that flag getting set? What code could have done it? When? And the answer is basically ANYTHING at ANY TIME that could possibly get a reference to the object. And web applications are far from the worst offenders in this space because the request / response cycle is (usually) fairly globally stateless.
Clojure is the exact opposite of that experience.
The state of a Clojure program will most likely comprised of data literals (think JSON data types, if you don't have experience with Clojure / Lisp data). Printable data literals. Connect to the errant server, serialize the state, read it from your machine, and you're there. It's coherent, stable, serializable, transmittable, simple.
Who can mutate your data? No one. You have an immutable reference (maybe others do too, but reading is a fundamentally safe operation). How does it change? Only along the explicit path of computations you're working through (it doesn't change, actually, you choose to hold onto a new reference to derived data when you want to).
Or, if you really need a mutable escape hatch (like, say, you're holding a handle to a database), every type of mutation in (core) Clojure has defined (and thoughtfully so) concurrency semantics. You won't see a bunch of notes in Clojure API docs that say things like "not thread safe" like you see in JavaDocs.
TLDR: Clojure will happily give you object-like read-only views into your database (like Datomic's `datomic.api/entity`), or help you write queries with a knowledge of your database schema, but most Clojure persistence solutions will explicitly coordinate mutation into a single 'site' because that's the only way maintain a coherent view of state-over-time. And that single-mutation-site story is the opposite of what ORMs (as commonly defined) do.
Typical ORM's are a mismatch for Clojure. Two pretty good options to generate SQL are honeysql and hugsql. They both attack the problem from two different directions.
+1 on avoiding side effects from business logic. This applies not just to Toucan, but to database code in general. Toucan is there to give you a way to define behaviors when interacting with your application database, for example transforming certain columns when you fetch rows from a certain table from the database. It's that plus utility functions for interacting with your database. Not really an ORM.
Either way, Toucan 2 is out now: https://github.com/camsaul/toucan2 I'm still working on fully documenting it but it's already being used in the wild and Toucan 1 will probably be archived soon
Clojure has a lot a faults (just look at some of the comments here) BUT and it's a BIG BUT for me...
Clojure is SUPER FUN :)
Over the years(15+), I've been coding in PHP (suck it haters), Go, Rust,Java,Python AngularJS+, Svelte and I can honestly say for me, nothing is more fun that coding in Clojure.
It's fun testing a function in "realtime" by just "eval" it on the spot. I don't even code "in the REPL" I just use Calva and eval inline in VSCode
Maybe it's cause it's my new toy but it really does bring back the "joy of coding" I've been missing in the other languages.
*Learning Clojure became much more easier, once I told myself "It's Maps All The Way Down :D" Sure there are stuff like atoms that make it possible to code around the "immutability" but that is like using a cheat-code to go back to the old ways. Yes there are many times when mutable-data-structures are required, but while learning, don't grab for them as a first solution !
Anywhoo YMMV but once you get over the warts (many there are), much fun and insights awaits :)