7 ms·
My thoughts after using Clojure for about a month
https://web.archive.org/web/20260603023617/https://www.acdw.net/clojure/ https://web.archive.org/web/20260603023617/https://www.acdw....
- gertlabs 5mo agoThe functional paradigm is a bit uncomfortable at first, but it does make problem solving feel... different. I personally find OOP to be the most intuitive for large scale systems design, but that's just me. Most models do not perform particularly well in Clojure, but OpenAI models fully utilize the power of the language. Subjectively, it kind of seems to match the personality. Data at https://gertlabs.com/rankings?provider=openai https://gertlabs.com/rankings?provider=openai
- andai 5mo agoNot sure if I'm reading this right, but the "success rate" table for OpenAI models shows Clojure near the bottom. And if I switch provider to Anthropic, success rate for most languages, including Clojure, goes up dramatically.
- gertlabs 5mo agoSuccess rate includes syntax/compilation failures as well as environment rule violations, and is almost entirely from one-shot code generations. Percentile shows how well the working submissions perform. In long horizon agentic coding evaluations, strong models fix the syntax and percentile and it becomes a direct comparison of which submissions per language performed the best on average. You can filter for that here: https://gertlabs.com/rankings?provider=openai&mode=agentic_coding https://gertlabs.com/rankings?provider=openai&mode=agentic_c...
- xoxolian 5mo agoThanks for the link! What would you say is missing from Clojure for large-scale OOP design? As I understand, Clojure gives you OOP a la carte. Objects (via maps/records/structs), polymorphic dispatch (via multimethods/protocols/case), types (via Malli/TypedClojure), inheritance (via derived, isa?, etc), some encapsulation (via defn-/^:private)...
- gertlabs 5mo agoI might just be a simpleton -- I never had the resolve to try an ambitious project in Clojure. I was not aware that you could get full OOP though, what you are describing feels like yes technically possible but kind of a hack to get inheritance / no type hierarchy enforcement. I'm no expert on the language though
- bcrosby95 5mo agoI actually disagree. Once you remove the cruft and crap of the involved syntax, good OOP design tends to look damn close to FP design. So I flip your point of view - class based OOP is the hack - despite not really using Clojure or FP in my dayjob or hobby projects anymore. Most fun I had with OOP was definitely Common Lisp though.
- chamomeal 5mo agoNot the person you're replying to, but have you tried TypedClojure? I've always thought clojure-with-types would literally be the perfect language, but I also read TypedClojure is more of a research project than a real language that you should use in prod.
- xoxolian 5mo agoNo sorry, of the things I've listed, I'd never seen nor heard of a project that uses Typed Clojure, nor probably inheritance via dervied/isa?. For static (partial) typing, I instead use Malli schemas. I do this for every larger Clojure program I make, because there's always something that needs paranoia, or it's handy to generate example data.
- chamomeal 5mo agoRaw models aren't as effective with clojure as they are with typescript or python, but clojure has a superpower that most other languages don't have: the REPL! Specifically nREPL and the ecosystem around it. An LLM is only as good as its feedback loop. If your LLM can actually test the code it writes, it's going to be much more effective. Static types are a form of feedback (if it can use the LSP), unit/integration tests are another. Clojure has an exceptionally good repl. LLMs can eval any piece of any function. They can test out functions they aren't familiar with. They can fetch data, try out different arguments, try different approaches before committing to one. They can query a database (read-only connection, of course), look at the result, fetch data from an API, and stitch it all together. It can even hook into your running program and debug it from the inside out!! It makes it so much more effective at using libraries or paradigms that it isn't trained on. In my experience, hooking an LLM up to the clojure repl lets it write WAY more complex stuff. I'm talking like 10x more complex programs with zero errors, cause it can literally try it out every little piece before putting it together. It's like watching a human programming. But like, really fast. Sorry I get a little ranty when clojure + LLMs come up, because I don't think most people realize what they're missing out on. It's crazy stuff. It's also easy peasy if you use vscode. There's an extension called calva-backseat-driver that just hooks it all up for you. Gives copilot access to the repl, and I think it exposes an mcp if you want to give claude access too.
- gertlabs 5mo agoGPT 5.4+ models are extremely good at writing Clojure, agreed. In the agentic coding part of our benchmark, they do have access to the REPL via bash if they choose to use it. Filtered here: https://gertlabs.com/rankings?mode=agentic_coding https://gertlabs.com/rankings?mode=agentic_coding
- gleenn 5mo ago> I personally find OOP to be the most intuitive for large scale systems design, but that's just me. The beauty of Clojure shines through when you want to change something that cuts through a large part of a large project. If you are using mutable data, you may end up with many bugs from various pieces of code mutating objects inconsistently. With Clojure, if someone hands you data, you can't possibly break some distant piece of code by updating an object: it's just not possible because you only ever make fast, updated copies. The more complicated your codrbase gets, the more this benefit is realized. I actually kind of think of it as an easier mechanism with similar outcomes to Rust's borrow checker. Only one piece of code ever owns the data so things end up much safer. However it is way easier to use IMHO because you just know that zero people own anything and everyone can read everything. It also makes converting some code to be multi-threaded extremely easily and with some constraints guaranteeably correct. Lots of dovetailing features neatly put together for both clarity and less bugs and more usable cores which are probably sitting idle.
- onlyrealcuzzo 5mo agoRich Hickey's: Are We There Yet may be interesting to any one here: https://www.youtube.com/watch?v=ScEPu1cs4l0 https://www.youtube.com/watch?v=ScEPu1cs4l0 I found this to be one of the more interesting talks I've watched. Like you (I think) - I love functional languages. But there's a problem I can't really figure out how to articulate where they reach a level where they stop "just working" imo. Maybe it's just me being too dumb.
- schonfinkel 5mo ago> I personally find OOP to be the most intuitive for large scale systems design, but that's just me. Once you're more comfortable with it and want to try a typed functional programming language, I highly recommend checking OCaml (or SML, if you're into old school tech) and see how the Module Functors are applied, most software will look extremely over-engineered after you write a few functors. It's the feature I miss the most when coding in F# or Gleam, for instance.
- drob518 5mo ago> I personally find OOP to be the most intuitive for large scale systems design, but that's just me. At one point, I was the same. But after going functional in Clojure, I can’t imagine going back. Using maps nd just having common functions that transform data into different data is definitely the way to go. This is with your time: https://youtu.be/aSEQfqNYNAc https://youtu.be/aSEQfqNYNAc
- jimbokun 5mo agoFunctional is far more comfortable to me. Trying to model all that state spread through out your program with no way to really isolate it or just reason about a small part of the program at a time, I find very stressful.
- Hammershaft 5mo agoIt's fascinating that Clojure has consistently the best performing solutions and yet at the same time such a low success rate. Do you have an idea as to why that is? If I had to guess, two things lowering reliability: A) Balancing parens might be tough on an LLM one-shot. B) LLMs generate tokens sequentially, but s-expressions mean the first forms to be evaluated in a body are usually the last to be written, so the LLM has to sequentially generate layers of evaluation backwards.
- xoxolian 5mo agoIf B), then maybe the LLM should be instructed to prefer things like the -> and ->> operators. So the first forms evaluated are also the first written.
- jwr 5mo agoFirst, we would need to agree on what "such a low success rate" means. Programmers have a thundering herd mentality: there are usually 2-3 "top things" that are in fashion at any given time and the herd tends to go towards these top things. They are not necessarily good or "successful" (however you define that term), they are just popular today. From my point of view, Clojure is a very successful language. It has been in stable development for >10 years now, with no major breaking changes (!). I was able to start a business using it and now make a living from it, all of it possible largely because Clojure reduces incidental complexity so much. Now, as to LLMs, I can see this discussion is mostly theoretical, so let me pitch in with data. I've been using LLMs for Clojure for a while now and it works fantastically, from what I read about other languages, quite a bit better for me than for others. Balancing parens was a problem for early LLMs without tools, Claude Opus with clojure-mcp tools doesn't encounter that problem at all. Additionally, the ability to try things in the REPL means that LLMs are very effective: all hypotheses and solutions are immediately tested, with automatic feedback. Overall I get great value from LLMs and I am able to solve large problems with them.
- deleted 5mo ago[deleted]
- 5mo ago
- HiPhish 5mo ago> I am now generating this website with Clojure As everyone knows, you are not a true lisper until you have written your own static site generator. It gave me such a great high with how easy it was to add my own "templating engine" on top, implemented all using macros. The downside is that the crash came hard; there is so much more to a good static site generator such as optimizing the output, supporting scoped CSS, server-side rendering of SPA framework components, and of course integration with the Node ecosystem (for better or for worse there is just so much useful stuff). I have since moved over to Astro. It's still fascinating how far I was able to push my own SSG all by myself though.
- embedding-shape 5mo agoHeh, inspired by hiccup, I ended up implementing my favorite Clojure templating library but in Nix, exactly for the purpose of static site generation :) Even have a nifty demo of how it looks for that, it basically looks/works the same as hiccup: https://emsh.cat/niccup/examples/blog/ https://emsh.cat/niccup/examples/blog/
- tymscar 5mo agoThis is awesome, thanks for sharing
- tmpz22 5mo agoYeah integrating NPM is the big one, then you’re whole day converts to recovering from breaches
- acdw 5mo agoOh I've written an SSG in multiple variations of lisp, as well as sh, make, and most other languages I toy around with. It's been a good "kick the tires" project but I think I need a new one.
- fp64 5mo agoFunny, learning Janet I exactly did that. Was quite a fun experience with the built-in PEG, so I did markdown parsing from scratch. Maybe eventually I will be a true lisper (fell in love with Scheme over 20 years ago but could never really use any lisp professionally. Now I at least do some small things in Clojure and babashka. I love babashka)
- meken 5mo ago> I do wish there were an easier way to move in the ]}]})))}-ness of block ends though. I’m not quite sure what this means. How is it different/worse than all parens..? fyi I use paredit and just hit ) and it moves me past any kind of paren/bracket. But even without that you can just hit left and right..?
- drob518 5mo agoExactly. Paredit for the win.
- everforward 5mo agoThey are a pain if they get unbalanced if you aren’t using paredit. Like if I vi delete the last line of a function out of habit it’s a pain to get them back in the right order. It’s easier if everything is parens, just hit paren til the errors are gone.
- meken 5mo agoAh I see. That makes sense. That’s making me really thankful to be a paredit user.
- everforward 5mo agoLearning paredit is on my list of things to do. I've been loving Clojure for hobby projects lately but my editing setup sucks for Clojure. The vi commands I'm used to using are bad in Clojure (eg copying by line almost always means unbalanced parens), and the autocomplete LLMs are very inconsistent about closing syntax correctly. It seems super cool, I just haven't done it enough for my brain to reach for paredit keybinds instead of vim chords.
- acdw 5mo agoI use Emacs's built-in structural editing bindings, which doesn't have the auto-move-past any kind of paren/bracket thing. Maybe I could add that in. But I was talking about like, when refactoring, I'll maybe change something from a list to a vector, and I have to change the delimiters at front and back. Or, where electric-pair does do the move-past-all-parens thing when I just spam ), it doesn't do that with ]}]}]]}}]})).
- pdimitar 5mo agoWith respect, this topic in particular has been beaten to death. I too liked Clojure when I tried it some years ago (agreed on the composition and data structures; both are _great_). But the real value-add is in the runtime, not the syntax. Java has a solid runtime but it's not yet as good as Erlang's, maybe even not up to the standards of Golang -- I am talking concurrency / parallelism here (for memory management I have no doubts Java is very good). And I know: green threads and stuff. Well, call me when you can do what Erlang / Golang can do. Then I'll look again, very seriously too. Programming language syntax scarcely matters. It does to some extent but we the programmers tend to over-romanticize it. The runtime and its properties are the much better thing to optimize for.
- iLemming 5mo ago[flagged]
- bcrosby95 5mo agoWhen it comes to concurrency, what can golang's runtime do that is so special? When I tried it, it seemed like a worse version of Erlang's for people that prefer C style syntax. Depending upon your design space pervasive immutability is a huge boon too and golang doesn't have that but Clojure does - Erlang obviously having that and more. I always wished clojerl took off.
- pdimitar 5mo agoI agree Golang is a worse version of OTP, no question about it, but if you are not allowed to code in Erlang/Elixir/Gleam (which sadly is 99.9% of the projects on the planet) then Golang is the next best thing. It has footguns, sure, but with library support and discipline it can get you very far. To me it's embarrassing that PLs still tout syntax and various other goodies, completely glossing over runtime. I might be missing something. But faux humble statements aside, I feel many others are the ones who miss something -- and that's the fact that doing stuff in parallel is a fact of life for 20+ years now and it's time all popular PL runtimes finally wake up to that fact. If not, I am simply not considering them. And I am not saying that arrogantly though it sounds that way; there are some PLs that I _really_ liked and was almost heart-broken that I had to abandon them and not work professionally with them. But I have enough experience to know that runtime choice matters, a lot. For the record, Racket was one of those PLs I abandoned. I know they started working on parallelism some years ago but I had to make a decision next week back then so, Elixir + Golang + Rust it is for me.
- BoingBoomTschak 5mo ago> The seq abstraction, for example, means I usually don’t have to worry about what kind of sequence I’m dealing with Eh? That's completely lifted from CL (https://www.lispworks.com/documentation/HyperSpec/Body/t_seq.htm https://www.lispworks.com/documentation/HyperSpec/Body/t_seq...). Same for AREF/NTH, there's ELT. Other than that, I agree, CL is baroque yet needs some hole filling here and there. > Lisp: everything is a list But that's wrong. Not even a little. Unless you mean LISP 1.5... > Too much syntax Funnily, I'm mostly okay with the new vector/set/hash-table literals, my big problem and that of some other people is the use of vectors in macros/special operators instead of lists. `(let [a b] ...)` instead of `(let (a b) ...)` is _not_ okay.
- y1n0 5mo agoI haven’t used clojure in quite a while but what’s the issue with (let [a b] …)? Is (let (a b) …) even valid clojure?
- everforward 5mo agoI believe it would be (let ‘(a b)), but I’m not sure if that’s valid or not. That’s how Racket does its version of defn
- acdw 5mo agoIn CL and Scheme, it's (let ((var1 val) (var2 val)) body...). So parentheses are used for grouping and function/macro application. In Clojure, parens are just used for application, so you have e.g. (let [var1 val var2 val] body...), or (defn foo [x] ..) or (cond testa 1 testb 2 ...). It takes some getting used to, and I do wish Clojure would do something more like (let [[var1 val] [var2 val]] ... .. though of course then you'd have to figure something else out for destructuring.
- midnight_eclair 5mo ago> `(let [a b] ...)` instead of `(let (a b) ...)` is _not_ okay it is however quite consistent, clojure uses vector form for most macros that require a "control" form before a "data" form: argument list in defn, names list in let, iteration descriptors in for, etc. you get used to consistency quite easily.
- NetMageSCW 5mo agoI wonder if the author is familiar with Smalltalk - it has a very small syntax. In some ways so does Lisp, in other ways it has more than every other language, depending on what you think about operators versus functions.
- acdw 5mo agoI have heard of Smalltalk and it is intriguing but honestly I don't even know how to get started with it lol
- smitty1e 5mo ago"Lua: everything is a table Tcl: everything is a string Lisp: everything is a list" Python: {"everything":"dictionary"}
- karolinepauls 5mo agos/everything is/everything seems to me/
- temporallobe 5mo agoI’ve been using Clojure for almost 10 years and it still feels like a foreign language to me. I call it “parenthetical hell”.
- Jeaye 5mo agoOnce you learn Clojure's syntax and semantics, you're no longer bound to the JVM. There's ClojureScript (JS), ClojureCLR, ClojureDart, jank (C++), Basilisp (Python), babashka (SCI), and many others. This means that, if you don't know Java or don't like the JVM, you can likely use Clojure wherever you already feel most comfortable. For the most part, any Clojure code which doesn't use host interop will work on all dialects. Clojure also has support for conditional code, depending on the current dialect. This is one of Clojure's superpowers.
- brazukadev 5mo agofor JS there is also Squint which is a light-weight ClojureScript dialect without the Google Closure Compiler
- JBiserkov 5mo agoindeed, light-weight means you just add a <script> and you're off to the races. There's also nbb if you're targeting node https://github.com/babashka/nbb https://github.com/babashka/nbb
- Hammershaft 5mo agoAs someone who loves Clojure, I wonder about the real portability across host languages. Do you have experience with any of these other dialects? (beyond the obvious CLJS & Babashka?)
- Jeaye 5mo agoI am developing a test suite for portability of clojure.core across dialects. You can find it here: https://github.com/jank-lang/clojure-test-suite https://github.com/jank-lang/clojure-test-suite Currently, we have Clojure, ClojureScript, ClojureCLR, Babashka, Basilisp, Phel, and jank running the test suite. I have only used Clojure, ClojureScript, and Babashka in production. But I am the creator of jank.
- rcarmo 5mo ago
- zuzululu 5mo agoThats nice but not many jobs for it
- jimbokun 5mo agoIf that’s your concern you don’t need to be concerned with any technical quality of any language. Just count job postings by language and learn the one with the highest value.
- zuzululu 5mo agoif there isn't any jobs for it it probably doesn't have the appeal or communicate economic value
- Hammershaft 5mo agoClojure programmers tend to be some of the highest paid[1] on average of any language, so I'd lean more towards a lack of appeal. [1] https://survey.stackoverflow.co/2019#technology-_-what-languages-are-associated-with-the-highest-salaries-worldwide https://survey.stackoverflow.co/2019#technology-_-what-langu...
- zuzululu 5mo ago> 2019 do a search for indeed jobs
- yolkedgeek 5mo agoJobs and market have a huge factor of 1. politics 2. synergy Almost the only reason that python is popular and people do everything with it is synergy. The only reason that javascript is eating the world and any web dev has to know it, and people do everything with it is synergy. A big reason for C# to have popularity is because it's for Microsoft (politics). These are just some examples. Being used doesn't mean anything. There were and still are a ton of PHP jobs. You tell me, is it because PHP is a great language and solves our problems? No, because it has synergy. If you're good enough, you can pretty much choose what language/framework you work in.
- ai_fry_ur_brain 5mo agoThat gradient tho
- tartoran 5mo agoToo Many Requests The page you have tried to access is not available because the owner of the file you are trying to access has exceeded our short term bandwidth limits. Please try again shortly. Details: Actioning this file would cause "www.acdw.net//clojure/" to exceed the per-day file actions limit of 80000 actions, try again later
- wartijn_ 5mo agohttps://web.archive.org/web/20260603023617/https://www.acdw.net/clojure/ https://web.archive.org/web/20260603023617/https://www.acdw....
- deleted 5mo ago[deleted]
- fbuilesv 5mo agoCached: https://archive.ph/lzYld https://archive.ph/lzYld
- roshanroyj 5mo ago[flagged]
- pjmlp 5mo ago> Clojure makes a big point of being a hosted language, that is, a language that runs on a premade runtime. This is why I am found of the community, the symbiotic approach of two language communities working together.
- shevy-java 5mo ago;; This is real syntax! (loop for k being the hash-keys using (hash-value v) of hash-table ...) Still lisp. Although the blog author has a point - clojure is probably cleaner lisp than common lisp. I think the issue is heavily due to syntax though. Naturally the (())()()()(), but I think even aside from the (), the syntax does not seem super-efficient to me. Perhaps I have spent too much time with ruby and python, but it feels as if lisp is a legacy regression, purely syntax-wise.
- draw_down 5mo ago[dead]
- pgt 5mo ago> "I do wish there were an easier way to move in the ]}]})))}-ness of block ends though." If he means navigating the AST, there is Parinfer: https://shaunlebron.github.io/parinfer/ https://shaunlebron.github.io/parinfer/ Paredit / Parinfer ruined other languages for me. It lets you navigate up/down/in/out of the Clojure AST with keyboard commands and mutate those expressions, e.g. "Split" will split open the current data structure you're in: `(a| b)` =Split=> `(a)| (b)`, where | is caret. Join is the inverse, and it works for all data structures.
- Sharlin 5mo agoIndeed. The incredibly clumsy way we choose to edit source code has baffled me since I was first acquainted with parinfer. Having to keep the source in the shape of a valid AST almost entirely manually is really annoying.
- evelant 5mo agoCheck out Ki editor https://ki-editor.org/ https://ki-editor.org/ — core movements based on AST
- embedding-shape 5mo agoHuh, very interesting and obvious in hindsight. What languages/syntaxes does it support though? Parinfer is lucky that it's relatively small amount of syntax, so can work for entire languages with relative ease, but how does Ki Editor work with Rust for example, if it does? Special keybindings per language?
- Sharlin 5mo agoLooks like it’s written in Rust and the examples are also in Rust :) Apparently it supports any language with a tree-sitter parser, which makes sense, tree-sitter is basically made for this.
- 5mo ago
- perarneng 5mo agoI spent a week with Clojure and coming from other functional languages my problem was not Clojure, it was dynamic typing. I got strange bugs in the standard library because I accidentally sent in nested incompatible instances of objects and it was really hard to figure out what was wrong in a quick way. With typesafe languages you are stopped at compile time.
- wellpast 5mo agoClojure was explicitly designed to be dynamic. It’s a feature, not a bug. https://clojure.org/about/dynamic https://clojure.org/about/dynamic Until you get better at not making mistakes that the training wheels of a static type system “protect” you from, lean into the REPL as a means to build up small correct expressions into larger ones.
- moi2388 5mo agoIt’s both.
- IceDane 5mo ago"Until you get better" is such an arrogant take. It's not just about skill. It's about maintainability, ease of refactor, and modeling invariants in your code in a way that they can be checked by the machine (the compiler) without every single developer having to maintain them in their head. Clojure even knows this is an issue and many people use `spec` to sort of retrofit static typing. Dynamic typing was, is and always will be a mistake. There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs. The worst part about Clojure is the community. Rich Hickey has cult-like status and the only thing clojurians can do is parrot his inane commentary.
- weavejester 5mo agoI too agree that "until you get better" isn't a good take. To err is human, and even the most experienced developers make mistakes. That said, you don't get static typing for free. As with many things it's a trade-off: you catch some errors at compile time in exchange for working within the confines of the type system. The ultimate hope is that the time you spend fiddling with types is going to be less than the time you spend debugging type errors. > There is nothing you can do with dynamic typing that you cannot do with a sufficiently powerful static type system - and it doesn't have to be something absurd like Haskell's. You basically just need structural typing and type inference and some type-level programming constructs. Haskell doesn't have a complex type system for no reason; it's necessary to encompass everything it wishes to do, and even then it's not as flexible as a dynamically typed language. For instance, how would you statically type Clojure's `assoc` function? It's not at all trivial if you want to retain the type information of the keys and values.
- rekabis 5mo agoHN hug of death? > Too Many Requests > The page you have tried to access is not available because the owner of the file you are trying to access has exceeded our short term bandwidth limits. Please try again shortly. > > Details: > Actioning this file would cause "www.acdw.net//clojure/" to exceed the per-day file actions limit of 80000 actions, try again later
- uxcolumbo 5mo agoWhy are iLemmings comments dead / flagged [0] [1]? Can't even reply inline and have to reply in the main thread. I found them informative. Lots of Clojure haters here I guess? [0] https://news.ycombinator.com/item?id=48375393#48387700 https://news.ycombinator.com/item?id=48375393#48387700 [1] https://news.ycombinator.com/item?id=48375393#48387382 https://news.ycombinator.com/item?id=48375393#48387382