129 comments
  • jwr2y

    As a heavy user of Clojure (my SaaS is written in Clojure/ClojureScript) for 10 years or so, I would fully agree with what Kyle wrote. In addition, things that I found very valuable:

    * most of my domain code is in cljc files, so these get compiled both server-side and client-side and I get a huge boost from that

    * transducers and transducer pipelines are an under-appreciated feature, I get huge mileage out of them, because of performance, composability and reusability

    * stability and longevity: Clojure was created by a very experienced system designer and programmer (Rich Hickey) and it shows. Backwards compatibility is hugely important, and I can focus on my application rather than rely on tools that are a moving target. It's not an accident that an average "Clojure programmer" (I dislike these kinds of tags, but let's run with it here) has 12+ years of experience and earns a salary in the top quartile at least.

    • drikerf2y

      Similar story. I'm also running my SaaS on Clojure and maintenance has been a dream compared to other languages. Things rarely break and are easy to upgrade.

      Developer experience is fantastic too with HMR, REPL and cljc files. It's fun to write Clojure code :)

      • packetlost2y

        What do you use for an editor/IDE? I'd love to write more Clojure, but the tooling story is a bit confusing

      • weikju2y

        HMR?

    • alberth2y

      What % of the overall benefits would you attribute to the JVM vs. Clojure (the language)?

      • jwr2y

        I barely even care about the JVM. I'm happy because it is an impressive feat of engineering, representing tens of years of huge investment into development, compiling my code to levels of performance that poorer VMs cannot reach. But JVM is just one of the environments where my code runs (the other is the JavaScript VM in browsers).

      • iLemming2y

        Clojure is not only on JVM. There's Clojurescript. There's ClojureDart. There's ClojureCLR. There a bunch of experimental things like ClojureRS (Rust) and Clojerl (for Erlang). You can "talk to" Python and R from Clojure. You can use Fennel if you need to deal with Lua (It's not Clojure, but it's very Clojure-like lang). The benefits are there, for any platform you choose.

    • DeathArrow2y

      Does Clojure have a decent web framework?

      • jwr2y

        I am not sure what a web framework is, to be honest. The choices for many parts of a web application are really domain-specific and I'm not sure a single "framework" would work for everyone.

        As far as web-related components go, my app uses Rum (as an interface to React), ring, http-kit, pushy (for history manipulation), sente (for websockets), buddy (for authentication tools).

        If you are looking for a batteries-included "I want to have some sort of webapp right away" thing, I think https://kit-clj.github.io would fit the bill, but the general feeling in the Clojure community is that unlike Python with Django or Ruby with Rails, the choice of app components is not predetermined by the language.

      • dgb232y

        Comprehensive frameworks include: biff, kit.

        Both of these rely on some of the most popular and simple libraries for web dev.

        Most seem to simply use said libraries directly, mix and match what is needed.

        If you’re an experienced web developer but new to Clojure, then you’re going to spend a good weekend or so to set up a proper environment, combine the necessary libs and so on, if you have some guidance. The Clojure community is quite welcoming and helpful in that regard.

        There’s an initial learning curve, because it’s a functional Lisp though. That comes with some necessary re-wiring, but also with some unique powers.

        The cool thing about writing Clojure is that feeling: „Really that’s it?“ when you realize how simple it is. It’s also simply fun to use, because of how interactive it is.

      • rafaelmn2y

        Because of it's lisp nature with special braces I find it to be nicer for creating DOM than HTML :

           [:div
             [:p "I am a component!"]
             [:p.someclass
               "I have " [:strong "bold"]
               [:span {:style {:color "red"}} " and red "] "text."]]
        
        really has that "first class" feel when you're working with it rather than feeling tacked on like JSX - at least that's how I remember it.

        My main problem with Clojure was the dynamic nature of it. I have not used it in very long time now (probably 7 years at this point) - but despite the amazing aspects of it I would not trade in TypeScript annotations.

        A question for clojurists here - what is the level of static type checking adoption in CLJ world ? When I was using it last time there were some things like schema validators, but nothing like TypeScript. It looks like there's static typing annotations support now but are the types widely used by popular projects/provided for big libraries ?

      • gleenn2y

        Luminus has been around a long time and Kit is newer. Most Clojurists believe in libraries over frameworks though so that's why you don't hear about web frameworks much. I've messed with Luminus a few times and it is really easy to pull out parts and swap in almost any bit of it. They document a lot well and make it really easy to pick different parts out of the gate like different DBs, JavaScript libs, and a lot more. The maintainer roams HN frequently as well :)

      • yogsototh2y

        Let me add my 2cents on this subject.

        I feel that due to its functional nature or perhaps this is just the community bias, Clojurists tend to prefer mixing lower level libraries to build their web application instead of relying on a specific big web framework like RoR for example. Luminus would be one RoR-like system.

        But today, I think people would probably choose reitit for the writing the API and use a clojurescript framework for the frontend or perhaps still use reitit to generate HTML pages using hiccup.

      • finnjohnsen22y

        "No, but maybe" is how I would answer this question myself. Maybe there is a working framework I'm unaware of - but in my experience using Clojure the last few years is that the concept of frameworks is not in the culture of the Clojure community.

        You compose your stack of smaller libs and build your own framework so to speak. The right questions in this context are; what http server to use, what routing lib is decent, what is a good lib for my database, what is a good lib for generating html from the server. etc.

        If you wanna start using clojure as a web server. Start with 'ring', and you can recieve http requests. You quickly realize you're missing stuff to do what you need and will go searching for libs. You'll find great libs out there for anything you ordinarily need for a web-server setup.

      • epgui2y

        I know you’re probably going to think this answer is crazy or stupid, but you don’t need a web framework in clojure. In fact you don’t need all the libraries that many languages need.

      • daveliepmann2y

        It has several. None of them dominate; most people assemble their own from individual libs.

      • 2y
        [deleted]
    • pif2y

      Out of topic, sorry!

      > Backwards compatibility is hugely important

      Why is this always considered a Good Thing (TM), apart when applied to C and C++?

      Why is it OK when languages and systems are bent backwards in order to avoid breaking stuff that works, but the two languages that together support a huge part of the running software are expected to up and break ties with their glorious past and present?

      • yogsototh2y

        Let me talk about my experience. I was (yes was) mostly an Haskeller. I loved using Haskell, and even a long time ago (in 2007 I think) when I learned it, the book to learn the basic of the language had snipped that no longer worked.

        But I still loved, it. And it changed every year, and I was even part of the proponent of the changes.

        But after a while, you realise that your old project stop working. That if you want to use a new library, you also need to update many dependencies. But each one has breaking changes, so you start updating your code.

        Mainly, if you wrote some code, and forget about it, it will no longer work or will be very hard to use with newer libraries. After a while this became very tedious.

        In Clojure, the code I wrote 10 years ago is still working perfectly today. The application still launches.

        If a bug is discovered and I need to fix it, for example, by upgrading or adding a new lib, I am not afraid to loose hours finding an fixing my existing code that suddenly become deprecated.

        So yes, stability is VERY important for me now.

        And last but not least, Rich Hickey talked about it in this talk and make a lot of very great points:

        https://piped.video/watch?v=oyLBGkS5ICk

      • aphyr2y

        There's a happy medium, I think, and I also imagine that depends on scope of change and the kind of applications involved. Speaking personally, I'm OK rewriting code, say, every decade. In the Ruby world, things moved so fast that my code would rot every six months. Now that I've been maintaining the same software for 20 years I'm really starting to care about that. ;-)

      • rhaen2y

        I'll also note a lot of objections to the way C++ does backwards compatibility is their adherence to an ABI which they refuse to break, but also refuse to say they'll never break.

        Many of the problem that can't be fixed for backward compatibility reasons are because they'd break the ABI, not new code. I think that's very different from other language's policy, which, from the ones I'm more familiar with, is about building old and new code together, rather than linking old and new code together.

        It makes for a much more restrictive, but also ambiguous and not guaranteed, set of requirements on any change.

      • wiseowise2y

        First one is a security nightmare, second one probably has more features and intricacies than other top three (by popularity) programming languages combined.

      • 2y
        [deleted]
  • whalesalad2y

    Clojure’s biggest issue is definitely the ecosystem. As far as the bell curve goes for accessible and usable tooling it’s usually either on the far left end and terrible but easily accessible (but doesn’t follow the functional paradigms well) or it’s on the far right end -extremely esoteric, LOTR quote in the readme, everything is data to the n-th degree, only accessible to dragons who can write emacs plugins blindfolded and upside down. The language itself is amazing, and Java interop is much better than Aphyr makes it sound. If there was a rails or Django for Clojure for instance (or better yet, a Phoenix) it would be an easier sell and methinks more widely adopted. The juice needs to be worth the squeeze - particularly when you’re convincing a team to jump from Algol style imperative languages to lisp. But at this time you need to reinvent the wheel on virtually any project, aside from fundamentals like http routing (which is also a rough scene) But the fact that literally any maven package can be used is tremendous. Plus it compiles to a jar, so anywhere a jar will run - so will clojure.

    • silcoon2y

      You should definitely check out Calva, a wonderful plugin for VSCode which bring the REPL and much more (live documentation) for an easy way to use Clojure. I'm using it everyday at work.

      https://calva.io

      • civilitty2y

        Situation: There are three competing Clojure development environments.

        Three!? Ridiculous! We need to develop one universal Clojure environment that covers everyone's use cases.

        Situation: There are fourteen competing Clojure development environments.

    • LouDNL2y

      You obviously dont code in Clojure for a loving. Clojure's eco system and community is well maintained. Clojure comes in multiple flavors where Clojure and Clojurescript are 2 mainly used. Ofcourse there is also Babashka, Scittle etc., but all derive from clj or cljs. As far as development tooling it all depends on the platform you code on and what ide suits your needs. Emacs, Neovim, VScode, IntelliJ for example have different tooling. Build systems differ and suits ones needs, for clj deps.edn is the current lost used followed by leiningen. For cljs there is shadowcljs and many more. I agree that as new comer this can be quite overwhelming, but the community is great and always helpful. I program for a living and have done so in multiple languages, Clojure is by far my most favorite.

    • raspasov2y

      "either on the far left end and terrible but easily accessible"

      Examples? I've been using Clojure since ~2013 and I can't think of "terrible + easily accessible" tooling.

      Most of the popular libraries are excellent. They are head and shoulders above in terms of stability/backwards compatibility/simplicity than most other ecosystems that I'm aware of.

    • travisjungroth2y

      This just seems like a random Clojure comment rather than anything to do with Jepsen or the article.

    • TacticalCoder2y

      > Plus it compiles to a jar, so anywhere a jar will run - so will clojure.

      I'd add, for those who don't know Clojure, that as /u/jwr commented in this thread: Clojure also runs anywhere there's a browser, thanks to ClojureScript. And thanks to source file that can be shared between both Clojure and ClojureScript (.cljc source files), you can have the very same Clojure code and run it both on the front-end and on the back-end.

      So not only the JVM "reaches" (as Rich Hickey says), but JavaScript reaches too.

      > ... only accessible to dragons who can write emacs plugins blindfolded and upside down

      I know you're joking but although I use Emacs since forever and can configure it to my liking and can eventually come up with some elisp code doing what I need, I'm not good at elisp by any means.

      But I'll admit that learning both Emacs + elisp + Clojure at the same time for someone who's never learned any Lisp dialect is going to be hard (and a very hard sell).

      Now there's a Clojure LSP server (which should help) and it works not only from Emacs but also from other IDEs. It's not mandatory to use Emacs for Clojure development even if many (most?) Clojure devs do use Emacs.

      • 2y
        [deleted]
    • DeathArrow2y

      I think the biggest issue is it being a rather niche language and not having enough adoption. That would make it harder to hire and more expensive.

      But if you can do it with a small team, it can work. Or you can do it in Clojure and rewrite at a later time.

      • sokoloff2y

        Or why not do it in Clojure and teach competent devs Clojure? (I think this is the Nubank approach, undoubtedly among others.)

  • ARandomerDude2y

    > Clojure...[has] no (broadly-accepted, successful) static typing system.

    I've used Clojure exclusively for 7 years and actively work on a Clojure + ClojureScript codebase of more than 500K LOC. The lack of a robust type system is my number one frustration. Many people point out that Clojure is expressive, lets you go fast, and generally just gets out of the way. I agree. It also lets you have some nasty runtime errors that can be hard to reproduce and debug.

    If there were a broadly-accepted, successful static typing system Clojure would be amazing. As it is, I think it's pretty decent if you like Ruby or Python and want more power. At this point I'm wondering if Go is the way.

    • erichocean2y

      You can integrate another JVM language like Kotlin into your Clojure project in places where you have measured that static typing would help.

      • munificent2y

        Snarky response, but semi-serious too: I've measured my own productivity and software performance and have determined that static typing helps everywhere. I've yet to measure and find any places that dynamic typing would help.

        (I say this as someone who has already spent the considerable time needed to understand static typing. For newer programmers or someone who doesn't program enough to make the investment worth it, I totally see the value proposition of dynamic types.)

      • joshlemer2y

        How do people measure whether static typing would help or not in any particular section of code?

  • kaliszad2y

    Can fully agree that Clojure(Script) really is a blessing and a great choice for many projects smallish or big. Frankly, Clojure is the first language I did professional programming in, with a little bit of experience in C, Python, PowerShell, Java, Bash, SQL before to solve university assignments or to help with systems administration/ some analytics of traffic dumps etc. We program OrgPad in Clojure+ClojureScript and it was a good decision, even as we have more than 100kloc of code. It is approachable and if I write a bit of code, I am fairly certain that it will do what I imagine. I didn't have that to that degree not even in Python, PowerShell or Bash, it is a completely different league, quite possibly game.

    The REPL workflow is really nice, my colleague did a video about it: https://www.youtube.com/watch?v=4igO7Qbyj9o

    Nowadays, even some functions/ short tasks or small projects can be served by Babashka/ nbb in places where you would often consider switching from Bash to Python.

  • rtpg2y

    Last time I spent a couple klines trying to write some clojure (+ clojurescript), the big thing was that most of the tooling had some really nasty error states. Python has some of this (ctrl+C out of most Python programs and get that stack trace with a bunch of inscrutible stuff), but given I was just starting out it was tough for me to move forwards.

    I do think the language + ecosystem has so many innovative and interesting ideas built in. Definitely worth at least reading the history of Clojure paper and internalizing most of the lessons.

    This is going to be anathema to everyone but I think there's a nice little ecosystem space for a scripting language that is as serious about performance and code ergonomics as Clojure... but without the parens[0] (and I could do without the nil-punning, but at least there's a bit of a foundation there)

    [0]: yes I know that (f x y) is nicer than f(x, y). My problem is more when it comes to things like let forms introducing indentation (I shouldn't have to pay an indent + paren cost when trying to give a value a name!) and other things that just feel like line noise when coming from a language like Python, that just uses the indentation I'm going to put in my code anyways as information

    • chii2y

      > let forms introducing indentation

      this is a symptom of using let forms too copiously, rather than using a functional style, where there's very few requirements to attach names to intermediate arguments being passed between functions.

      Often, you would use threading macros (the -> and ->> stuff) to implicitly pass arguments from one function to the next, or define transformations which are themselves just pure functions (so you would define it at the top level).

      It's understandable that some interop require a lot of let forms - mainly those that mutate an argument in a chain of procedures (looking at you, java jwt libraries...you know who you are).

      • rtpg2y

        (This is a rant, i get what you’re saying)

        I just disagree about me being the issue here. Sometimes I want to make names. They are basically documentation. This is just so normal!

        Like you can’t just say everything should be written in a point free style. There are times that is more clear and then times where having a name to a value makes subsequent code clearer. Especially given that Clojure has some of the world’s most anemic comment support (no real multiline comment support without futzing with forms? In this economy?)

        I do agree threading helps by making things more pipeline-y at times though. I just feel like being punished for verbosity is annoying. The verbosity is punishing enough, just let me write it!

      • skrebbel2y

        Well-named intermediary variables often help tremendously with readability though. A language discouraging their use seems like the wrong trade-off decision to me.

      • da39a3ee2y

        It’s essential to use names in programming to modularize code and introduce different levels of abstraction. Smart people can drift towards a point-free style because they enjoy the feeling that they are capable of comprehending their code. It’s important to resist the introduction of such code and to help such people see that neither they in 6 months, nor their colleagues now, will be able to understand it.

    • fulafel2y

      There's definitely some things you learn from mistakes when using a REPL workflow in Clojure or Python, where you debug for a bit and then realize that you just didn't reload or reinitialize something. I feel most people end up adding it to the set of things to sanity check when hitting "can't happen" situations, like "let's add a debug print here to make sure my code change is actually invoked". But that's just a backstop of course, you mainly use other practices in your REPL workflow to keep from hitting it.

      • rtpg2y

        I was thinking more about tools like `lein` just barfing up garbage when I did something wrong. This stuff might be totally better nowadays though! It's been many years since I touched the language.

    • freedomben2y

      > Last time I spent a couple klines trying to write some clojure (+ clojurescript)

      Killing me! My brain says this should be (+ clojure clojurescript)

  • 7275647970697062y

    Clojure does many things right and I like it for it a lot.

    One thing that I see slowing down real teams building real web stuff with Clojure is the near-dogmatic desire to compose (dare I say hack) everything together from tons of libraries that each have their own understanding of how things should be.

    I bet Clojure would be much more popular and nicer to work with if the community would _also_ (i.e. in addition to the current approach) have actively-maintained, batteries-included, generally usable web framework(s). (Is Biff it?)

    So that there would be alternative path to choose for us who like the language, but are not fans of the build-your-own-monster approach.

    Disclaimer: I have been spoiled by Ruby on Rails

    • fulafel2y

      Many of us see Clojure's appeal as connected to avoiding tying things together unnecessarily. Don't complect, compose, etc as in the famous Rich Hickey talk (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...).

    • Capricorn24812y

      Biff is very batteries included, but because it uses a couple defaults that are not particularly mainstream (XTDB instead of SQL, HTMX/Hyperscript) I imagine newcomers will still have a hard time in it. Learning Clojure, AND graph databases, and potentially HTMX is a bit much. Still, a lot of it is just setup for you. Auth is thoughtful and easy and the server config files are well commented. There are deploy scripts to setup a server in linux and SSH into the production REPL. And if you want to use XTDB, the way Biff has setup document validation is really nice.

      I also like using it more than an app running on ClojureScript, personally. There are a lot of ClojureScript wrappers around React, and it's almost a meme. As a react developer, I have not found any of them easier than React. I find them kind of convoluted and un-ergonomic. However, HTMX/Hyperscript works really well with Clojure, because their HTML syntax is just arrays and objects that are easy to manipulate. It's the first time that the whole "code is data" proverb repeated by Clojure enthusiasts has rung true for me.

      • jacobobryant2y

        First-class SQL support will be really nice when XTDB2 becomes stable--I'm planning to make that the default in Biff for the sake of familiarity, then anyone who wants to can switch over to Datalog easily.

    • gleenn2y

      I commented above but check out Luminus or Kit.

    • poidos2y

      I like Biff the most out of the ones I’ve tried.

  • dig12y

    > Clojure does have significant drawbacks. It has a small engineering community and no (broadly-accepted, successful) static typing system.

    I tried to dabble with OCaml multiple times in the past, and what I didn't like was the overly strict type system. I spent more time trying to please the compiler than writing the code. With Clojure (Common Lisp and C), I have more freedom to bend the compiler (type system) to do what I want it to do. Sure, there are pros and cons to that, but I don't want distractions when I write the code. It may be my style, but I meet numerous people who think the same. I believe (static) type systems are a matter of preference, and another camp thinks otherwise. However, it is always lovely to have opposite camps around because we learn a lot from each other.

    > Working with JVM primitives can be frustrating without dropping to Java; I do this on occasion.

    Rich (Clojure author) mentioned multiple times that Clojure is a hosted language, so it is OK to jump into Java when you have to (or javascript). IMHO, this is one of the most powerful features of the Clojure ecosystem because it is way more pragmatic. I've seen numerous attempts to re-implement the same thing repeatedly in your favorite language just because you don't want to touch "a library written in That Other Filthy language because it is slow/insecure/not-flexible/you-name-it". This was/is very frequent in Common Lisp but also in the Rust communities.

    • tmtvl2y

      Having type checking is indeed a matter of preference. I personally quite like having the language protect me from making silly mistakes, which is why I use type declarations liberally in my Common Lisp code. With SBCL doing its best to ensure they hold it has probably saved me minutes of headaches each project.

      Also CL is very pragmatic and nobody bats an eye at, for example, woo being based on a C library.

    • Karrot_Kream2y

      > I believe (static) type systems are a matter of preference, and another camp thinks otherwise.

      It's funny, when I led high reliability teams at a Big Tech, I wanted static strict typing everywhere done yesterday. Every production problem of ours was related to small, subtle issues. Upgrading the runtime leading to JSON key reordering breaking some ancient client that was hand-unrolling JSON. Some library deep within our stack started returning HTTP codes as ints and not strings which broke some error parsing of ours. And every outage was a big deal as the scale we operated at meant that if even a small percentage of customers were affected, we'd have hundreds of problem reports.

      Now that I'm working at a tiny startup things couldn't be more different. We chose to go with a static language because that's where the mindshare seems to be, but I am missing working in a dynamic language. Because of the speed with which we're shipping our code quality is really bad. In the interests of time, instead of dealing with the type system and refactoring things cleanly, we'll just make multiple copies of the same object structure with small differences and use them all over the place. We copy/paste the same logic in 10 different places instead of isolating the functionality and referring to it in different places, because the shapes of types make things a bit tricky to make generic.

      I find that different problem spaces lend themselves to different solutions. Are you getting paged in the middle of the night for subtle bugs? Then strict static typing is probably for you. Are you iterating quickly and can problems be solved by someone sending you an email or Slack? Dynamic typing is probably better. The trick is to not accumulate too much debt in the dynamic paradigm in case you see growth to port things over to a static paradigm.

      > I've seen numerous attempts to re-implement the same thing repeatedly in your favorite language just because you don't want to touch "a library written in That Other Filthy language because it is slow/insecure/not-flexible/you-name-it". This was/is very frequent in Common Lisp but also in the Rust communities.

      I think languages with lots of enthusiasts just end up with these attitudes unfortunately.

  • masijo2y

    Clojure is my favorite language by far, it feels natural to me.

    That said, the two things which I truly miss from other languages are: understandable error messages (as mentioned by other people) and typing hints.

    I've had to refactor large code at work and it's been a pain. It's hard to know when a function will throw an exception or just return nil, it's hard to know if a core function will return a vector, a sequence, or another type of collection, and so on and on. There are some projects that try to address this (spec, malli, typed clojure) but I haven't found them to be a good replacement to something among the lines of python/typescript typing.

    • Spivak2y

      Maybe I'm just spoiled by modern languages that have this and the ones that don't are bolting it on (Python/TS) but how do you put up with it? Any language that can't tell me the what goes in / comes out of a function feels borderline worthless for a codebase more than a few files.

      Trying to infer types by looking at what the current code appears to be able to do the objects is just pain for its own sake.

      • cushpush2y

        Actually, Clojure has typehinting! It looks like ^String or ^Int but usually it is not required because the compiler is very good at determining things. You can use the Clojure function (typeof? the-object-goes-here) to get the type. It is not a type-less language, it is a language of heterogenous collections and therefore lax type rules because they gotta go into the same collections together. The collections can be built in creative ways, so using fundamental elements to make larger nested collections is direct and possible. We can always ask what the type of an item is, and we can introspect contents, it's not as cumbersome as some people make it out to be via hn comment.

      • kennytilton2y

        I have written a couple of 80kloc Common Lisp apps. Not a problem. But we all are used to what we are used to, and anything different offends.

  • silcoon2y

    For all of you new to Clojure, check out this interactive tutorial: https://tryclojure.org

  • leoszliard2y

    Also, Clojure's fun

    • resonious2y

      This is underrated. I know it sounds unprofessional, but funness is also the reason Basecamp and Ruby on Rails was built in Ruby. Rails dominated web dev in the 2010s and tons of super successful startups were built on it. So while many folks like to scoff at engineers "playing with toys", a lot of good things we have now used to be toys (edit: Linux is another good example of this)

    • scary-size2y

      I think that captures perfectly! I've actually written about that sentiment in the past [1]. The tricky thing with a very "fun" language is that you assume that you can solve everything with it. You end up writing a lot of code, when, in the end, no code would have been an acceptable solution.

      [1] https://franz.hamburg/writing/clojure-makes-happy.html

      • wavemode2y

        lol sounds like you need to ditch paredit for parinfer

  • swiftlyTyped2y

    I love clojure, but over time I've learned the painful lesson that at the end of the day, for complex modern applications, the ecosystem is all.

    That said Clojure ecosystem isn't too bad

  • scotty792y

    > The error messages are terrible. I have no apologetics for this. ;-)

    Could something be done about this? I tried to learn Clojure but I bumped into that fairly early on and gave up. Rust was way more approachable for me because good error messages form learning dialog between the learner and the language.

    • phyzome2y

      It's sad to hear this is still an issue.

      For those who don't know, Clojure's error messages... aren't. They take the form of a stack trace from the compiler's internals, and that whole stack trace just gets barfed out onto the terminal. There's an exception message but it sometimes just isn't that helpful.

      I know there was an effort a while back to improve the situation, but last I saw (2019) I believe things were only marginally improved. It's a shame because other than that it's really quite a fantastic language.

      • aphyr2y

        Tbh I think they've actually gotten worse in recent years. This thing where the compiler spits out a stacktrace to /tmp and makes you go and cat it? Absolutely awful.

        And the move to spec, ugh, UGH. I have wasted so much of my life looking at spec errors for (e.g.) namespace declarations and being completely unable to tell what was wrong. I don't know how to fix this, unfortunately.

      • smw2y

        Isn't that true in most common languages? Python and Ruby and Java and Go (panics) will all spit out the stacktrace?

    • dgb232y

      Depends on the tooling you use and how you program.

      An REPL that is integrated with your editor is key.

      A good linter such as clj-kondo (comes with Calva on VSCode), helps a lot to avoid typing errors and a lot more.

      Using a REPL middleware which visualizes your output such as portal is very nice.

      One issue is when you get errors that involve anonymous closures. It might be harder to navigate those errors. But I don't know of a language that does this well. Clojure makes it very easy to divide and conquer though.

      Another big one involves macros. Macros are powerful, but errors you get from them can be quite cryptic in some cases. Usually for common macros there are linter rules that you can use/find to avoid those.

      Clojure itself has a symbiotic relationship with nils. However as soon as you interact with Java data structures you might run into exceptions here. It's something to be wary of.

      Finally, spec and malli give you tools to adorn your code with little specifications to state your assumptions and guarantees. You can automatically run those while you're writing code to get sharp, human readable error messages.

      So yes, Clojure error messages can be scary/confusing at times, even though it has gotten better. But there are a lot of things you can do, or have been done for you that really help here and sometimes go quite a bit beyond of what you'd expect from a more mainstream language.

    • sometimesweenie2y

      When I was learning Clojure, I just sorta dealt with it. Usually you are presented with a trace that contains a line number for the error which was enough. I’m not entirely sure what could be done about the error messages but with experience and more idiomatic usage it’s infrequent for me to get stuck on these (and when I do I just step through it in the debugger).

  • 38529977thrw2y

    It would have been worthwhile for them to mention why not use Java. I mean, grep for Java and JVM and that short blog lights up! It was good that they did stress the (very) small team size for the product as well. This was an aesthetic choice and I wish he had stressed that.

    Java remains an excellent choice for concurrent systems, with a commonly accepted static type system, and all "java.util.concurrent" and the rest of JVM are native and the language and tools do scale to even monstrous sized "teams".

    • aphyr2y

      I've been a Java programmer for ~18 years! A nontrivial portion of Jepsen is written in Java too. :-)

      Many of the points I touched on in the post are direct critiques of Java, but maybe I should be explicit. An obvious factor is size--when I've written the same project in both languages, Clojure usually winds up 5-10 times smaller. That's definitely improving as Java adds streams, lambda expressions, and so on, but it's still a good deal more verbose. That gets in the way of the kind of exploratory programming I do, especially at the REPL, and it's harder for me to read and maintain a giant codebase. I appreciate having fewer nouns, in general: Clojure emphasizes a uniform way of traversing and transforming data structures, and in Java every single class has its own way of representing its data, and many of those ways are bad.

      The standard collections library is full of mutability and thread safety pitfalls. Printed representations of datatypes are verbose. It's not particularly good at dealing with highly polymorphic data structure transformation, and it's not a particularly good static type system either--a good part of my Java Brain (TM) is devoted to knowing the mechanisms of erasure, memory layout, and when unsafe casts are actually the right choice. Functions are sort of becoming first-class with method refs and functional interfaces, but it's nowhere near the convenience and flexibility of a Lisp-1. Accessing the compiler at runtime is a pain in the ass. No hygenic macro system makes things like custom iteration or compositional error handling a pain. There's no analogue to Clojure's protocols, which are a fantastic tool for "Hey, compiler, I want a monomorphic-cached type-dispatch polymorphic call site here for a type hierarchy I don't control". Etc etc.

      There are things I love about Java. Automated refactoring is easier, and I generally like the level of IDE support. Nearly anything involving primitives, I drop to Java. Ditto, APIs that require annotations. Interface specification is more rigorous. Etc etc. That's why I write in both languages. But most of Jepsen is in Clojure for good reasons. :-)

      • 38529977thrw2y

        Thanks for the thoughtful reply. Always interested in your thoughts on software matters.

        You know my professional issue with Clojure is that it attracts poseurs. It's that strange spot in PLT that to appreciate it requires sophistication and experience yet barrier to entry (contra say Haskell) is much much lower and does not require the same. So you can get bragging rights without being someone like you who actually understands the cost equations in toto in context of picking languages. This human factor coupled with the technical matter of Clojure not being ideally suited for large scale code / long running / typical IT fubar realities.

        You mentioned macros. I can tell you about 'sacred macros' that must not be touched :) Clojure may have addressed software compatibility but it has a human resources compatibility issue.

      • Capricorn24812y

        > Nearly anything involving primitives, I drop to Java

        Can you describe what you mean by this? Does this just mean you're using native Java data types sometimes when speed is a concern? Is there an example somewhere in Jepsen?

  • jgrodziski2y

    We also write about our choice of Clojure for application development here: https://defsquare.io/blog/why-we-bet-on-clojure

  • koito172y

    In short:

    - being on JVM means Jepsen can use JDBC drivers to interact with databases

    - Clojure was designed to facilitate concurrent programming (via software transactional memory and persistent data structures)

    - Clojure's interactivity allows quick prototyping

    - Clojure's community has a strong emphasis on backwards compatibility

    - macros allow a great deal of code reuse

    - while there are some big drawbacks (niche language, no strong static typing, ...), it isnt an issue in practice since Jepsen is only worked on by 1-3 people at a time

    I agree with most of these points, but it seems overly simplistic of an explanation. I really wanted to see what other languages were used for prototyping and why they were deemed insufficient. For instance, Haskell is mentioned but the most treatment it gets is that it was too "dogmatic" for writing Jepsen. There was no comparison on how easy it is to interact with databases from Haskell as opposed to Clojure. Moreover, Jepsen was prototyped "in a few different languages", but they are not compared to Clojure, let alone mentioned.

    • aphyr2y

      DB vendors generally don't write Haskell clients, which put a serious damper on my "test lots of databases for a living" project. Love Haskell as a language, but it's just not a popular target, and I wasn't brave enough to go for C interop. As I touched on in the post, Haskell is also rigorous about side effects--I've found it difficult to, say, inject side-effectual performance monitoring into an otherwise pure piece of code so I can profile it. Maybe Haskell experts are better at this, or the story is better now.

      There was a Ruby version of Jepsen too. That enjoyed better library support and language flexibility, but it suffered from terrible performance, a standard library built for mutability, and a janky thread model. There was, at the time, essentially no way to reliably time out a Ruby thread. That was a dealbreaker all in itself.

      I wrote some of Jepsen in Erlang too, but abandoned the project quickly. It's got a really good concurrency story. A VM designed for concurrency and failure with bounded reduction queues--honestly this is something I miss in the JVM. I love Erlang as a language (and I think some of my Erlang might still be in prod!) but it also suffered from a lack of client support, not being well-suited for high-speed mutable state, and generally terrible string manipulation. There's a lot of strings in Jepsen. ;-)

    • travisjungroth2y

      Based on the title and intro, I think part of the point of this post was to have something to link to for a “good enough” answer to this common question. I’m guessing he wrote this relatively quickly. It was a choice made over ten years ago. Trying to recreate the decision would likely be rather time consuming, prone to mistakes, and really only invite more discussion.

    • fulafel2y

      Indeed the protoyping takeaways would be interesting. It's quite rare in real world apps to prototype a system in several languages to pick one. From the text, to me it seems more likely to me that there were several prototypes for other reasons, and they might not be functionally or architecturally equivalent enough for direct language comparisons.

    • 2y
      [deleted]
  • amai2y

    Being a Lisp dialect for the JVM Clojure has readability issues: https://tonsky.me/blog/readable-clojure/ That alone will make sure Clojure will always be a niche programming language and ensure job safety for the few ones who can read it. But nowadays Clojure brings no advantages anymore over Java. I wouldn't start any new project using Clojure.

    • iLemming2y

      Readability? Every programming language suffers from "not enough readability". There doesn't exist any language that is always clear and easy to reason about. Writing is fundamentally a method of communication that involves the use of symbols to convey ideas, thoughts, stories, information, or messages. You can mess it up in any language. Clojure, being a Lisp dialect, does not have readability issues. Au contraire, I believe it is far more readable than any other general use, popular language.

      Once you grok the key features of any Lisp, you may find Lisps (especially Clojure) to be clear and direct, and here's why:

      1. Uniformity: Lisp languages follow a consistent pattern. Functions and operators always come first, followed by operands.

      2. Minimal syntax: The minimal syntax can also be seen as making the language cleaner and simpler, decreasing the chances of errors.

      3. Expressiveness: Lisp allows you to accomplish more with less code, which leads to fewer lines of code and thus potentially easier maintenance and readability.

      4. Macros and meta-programming: The ability to extend the language by defining new syntax or deriving new languages can lead to more readable and concise code. This one's pretty much up for debate, because as easily as you can improve the language, you can also make it much more confusing. You know what Spiderman's uncle says.

      People using non-lispy languages often get excited about certain things, to some of them I can only react with raising eyebrows. I always have a Clojure repl around. Even when I'm not programming in Clojure. Like for example, if I'm testing REST endpoints and sending data back and forth, I configure it in such a way that it always converts the responses to EDN. EDN is far more concise and much more readable than JSON. With the connected REPL I can quickly `filter, map, reduce, group-by, sort, remove`, dedup, slice, dice, and serve the data any way I like. It will take you longer to go through jq's documentation to perform the same data manipulation with JSON. It's an extremely powerful way of dealing with data. With large quantities of data. Clojure is the only language that can turn any impossible-to-navigate-through data and make it precisely readable for me. Readability? Really? Spend some more time using Clojure, you may get surprised how insanely readable it is.

    • kazinator2y

      Note that the nil/false issue he refers to in a real Lisp in which nil is the one and only false value.

      He's basically saying, in Clojure, don't use time-honored, clear idioms that are perfectly appropriate in some other Lisps.

    • j-pb2y

      I'd consider your point more if you could actually write the name correctly.

  • jrvarela562y

    I have been acquainted with Clojure for a few years now. Haven't been able to 'break in' despite several attempts.

    My main conclusion is this: the language/workflow make developers way more productive compared to other modern stacks (I do Python/Ruby/TS on my day to day). The biggest, unsolved problem is http://www.winestockwebdesign.com/Essays/Lisp_Curse.html

    tl;dr It is so easy to implement things in Lisp so you end up with a myriad of half-baked solutions and no long-term support of dependencies.

    • iLemming2y

      I'm not sure if the Lisp Curse can in fact be easily applied to Clojure. Clojure projects, for some reason (in my opinion), don't suffer as much as projects in other Lisps - such as Common Lisp and Emacs Lisp. Maybe it's because of the hosted nature of the language, there's usually a good set of libraries to choose from for Java and Javascript, and my guess is that this would hold true for ClojureDart and ClojureCLR as well.

      You can typically find some Clojure/Clojurescript wrappers if you want to interact with a JVM or Javascript library. But if a wrapper doesn't do what you need, you can always use interop directly.

      Also, Clojurists tend to adhere to some good stylistic defaults. They try not to overcomplicate things without good reasons. Furthermore, the nature of the language forces you to keep things small and simple.

  • yayitswei2y

    Love that Rich jumped in in the comments!

  • barrenko2y

    There is no Rails for Clojure because Clojure is Rails for the right kind of software architect. Clojure is a language that beat the crap out of me but in a positive way. I don't use it, but learned a lot about what is possible.

  • avbanks2y

    "Almost every database has a JVM client, typically written in Java, and Clojure has decent Java interop."

    Curious to know in what ways could Java interop be better in Clojure?

    • aphyr2y

      Clojure's Java interop story is generally really good. Method calls are predictable and uniform. The collection types generally work both ways, rather than being a completely independent type hierarchy (as a former Scala programmer, what a breath of fresh air). Functions work as Runnable, Callable, Comparator, etc. Reify, deftype, and proxy are sufficient for maybe 98% of APIs I've worked with.

      There are, however, those rare exceptions! For instance, some Java APIs literally won't work without annotations. There's just no way (last I checked) to express those in Clojure.

      I don't remember exactly the nature of this problem, but I've had occasional issues with deeply overloaded argument dispatch before. Maybe it was something like "The Java API expects an object x which responds to x.foo(long l) and also x.foo(int i) differently", and there was no way to convince deftype to emit the right kind of class.

      AOT is... it's weird. You want ahead-of-time compilation when, say, you want to write a Java class that uses datatypes defined in Clojure. But AOT, at least in Leiningen, tends to break stuff in truly inscrutable ways. My professional opinion is that it's haunted.

    • Capricorn24812y

      I'm not the most knowledgeable on this, but in general, Clojure has smart ways to convert data types from Java types to Clojure ones. Just like Clojure lets you use functions on a lot of different data types (mapping over vectors, objects, etc), it lets you do the same with Java collections.

      Instantiating objects and using their methods is pretty terse and easy when using macros like ->

      (def method-result (-> (SomeClass.) (.someMethod "arg1")))

  • fuzztester2y

    sorry guys, i don't know clojure, so i'll write this script in my favorite programming language:

    #/usr/bin/english

    if clojure is mentioned, can rich hickey be far behind?

    and if rich is mentioned, can this video of his be far behind?

    methinks not.

    lo, and behold, said video:

    https://youtu.be/f84n5oFoZBc?si=Fy-1QcLkIiXVnqPo

  • contrarian12342y

    out of curiosity, does it use core.async or manifold for concurrency? (and what are the tradeoffs)

    • aphyr2y

      Nope! For a few reasons Jepsen sticks very closely to Traditional JVM Threads.

      Part of that is because Jepsen isn't hyper-concurrent. Most concurrency errors manifest in just a handful of client threads, and the JVM will run ~10K threads without too many issues.

      Another part is that I spend a lot of time debugging and profiling. Core.async turns stacktraces into Swiss cheese--it has to, to perform its implicit continuation-passing magic. But that can make it really hard to figure out Who Called What When, and Why Is This Process Slow. Standard blocking IO calls are also something that my profiler (YourKit) can analyze well.

      Finally, I invest a lot of time into tuning Jepsen for speed, and manage the handoff of data between threads carefully. In a system where actors were handing off state constantly core.async would be a bigger help, but there's really only a couple shapes for concurrent handoff in Jepsen, and they're addressed by either j.u.c. queues, forkjoin-style structured concurrency, or Weird Transactional Threadpool stuff.

      For a little more on this, take a look at https://github.com/aphyr/dom-top or https://github.com/jepsen-io/history/blob/main/src/jepsen/hi...

    • stefcoetzee2y

      Seems like `core.async`: https://github.com/search?q=repo%3Ajepsen-io%2Fjepsen+core.a...

      (As an aside: Missionary is a higher-level, more recent option in this area.)

  • dsign2y

    This is what happens:

    - Dev Foor Barry needs to solve problem X. Mr. Barry thinks about the problem for a long time, picks a technology stack YZ that is suitable to the solution and to his experience, and goes to work.

    - Some time later, Mr. Barry meets dev Alice Mallory. She didn't know X was a problem, and she didn't know Mr. Barry either. But less than a minute into their conversation and being a mutual acquaintance, Ms. Mallory is convinced that technology stack YZ is the WRONG tool for the job. She can't stop herself from asking "Foor, why YZ? Wouldn't it have been better to use KY?"

    I've been involved in this ploy (sometimes as Mr. Barry, and sometimes as Ms. Mallory) any number of times, for any number of technologies, from mainstream ones to more obscure ones. For the last few years, I've been trying to act more as Steve: "Use whatever you want." I have to admit though, that kind of acceptance is a difficult, uphill spiritual path. And I have met plenty of architects in their sixties and seventies who saw the hill and took a wide berth, and keep acting like Mallory.

    • 2y
      [deleted]
    • roughly2y

      I really appreciate Aphyr for being one of the only people I've seen to fully integrate their entire selves into their public persona and accomplish success. The Jepsen reports on any given system are effectively canonical documentation for a product, and Kyle's work and writing on concurrency overall is fantastic. At the same time, there's no "suit and tie" Aphyr out there - you read the blog, you get the weird; you follow them on social media, well, you get the Full Aphyr. I respect that they've been willing to put their whole person out there and have been so goddamn good at what they do that they've been successful at it - it's genuinely inspiring.

    • 2y
      [deleted]
  • effnorwood2y

    [dead]

  • qsort2y

    Sort of a meta comment -- the article starts saying that people keep asking "why Clojure".

    Why such an emphasis on what language stuff is written in? Why does it even matter? Sounds like it was a good engineering choice given the constraints of the problem, get over yourself ffs.

    • simongray2y

      It's probably because the author keeps getting comments from other developers about his choice of language and he's tired of explaining why.

      There was a HN submission yesterday about another project of his (also in Clojure) and many of the comments were "Why is this made in Clojure?".

      Unfortunately, a large segment of developers think there is no value in venturing beyond C-style languages.

      Another dimension in recent years is the static type checking cult, whose adherents must warn everyone else in the cult whenever they spot any kind of dynamically typed programming language, e.g. causing a separate thread about the lack of static type checking in any comment section about a Clojure project.

    • p-e-w2y

      > Why such an emphasis on what language stuff is written in?

      Because it's by far the most important decision made during the creation of any software project, and to a large extent, it sets the boundaries of what the software will ever be able to do.

      Experience also teaches that language choice strongly correlates with overall project quality. When I see a CLI project written in JavaScript (Node), I know that it's probably not worth my time to take a closer look. With Rust, Clojure, Haskell, F# etc, the opposite is the case. This is a cultural effect, rather than a technological one, but that doesn't make it any less real.

    • NateEag2y

      > Why such an emphasis on what language stuff is written in? Why does it even matter?

      polyglot programmers are the exception, not the rule, and even for people who have mastered >5 languages, there's still no one who's mastered the top 50, let alone all the languages out there.

      Language impacts both codebase contributor pool size and codebase reviewer pool size. In large, conservative orgs, the latter can matter a lot - good luck getting an insurance megaco's security review board to approve the cool new tool you found that's written in Eiffel - none of them know it, so none of them can review it intelligently.

      Language diversity is a good thing, IMO, and there are many different factors impacting what you choose, and that makes your choice more important, not less.

    • tasuki2y

      I don't understand your comment.

      Of course people ask "why clojure", we're curious!

      > Why such an emphasis on what language stuff is written in? Why does it even matter?

      Because different languages (and especially different paradigms) are different. The choice of language affects the project's future prospects, productivity, expressivity, maintainability, developer satisfaction, ease of finding developers, and a million other things.

    • contrarian12342y

      Bc the whole "data-drive development" is fundamentally a pretty different programming paradigm. You can do similar things piece wise or with a library in other languages but the Clojure language and all your dependencies are structured around it in a cohesive way

      You wouldn't be able to translate Clojure to another language line by line unless you brought in some framework or library

    • justin662y

      > Why such an emphasis on what language stuff is written in?

      People are interested in the decisions that informed the engineering of a tool they respect.

      People are interested in reading the code but aren’t going to learn something as goofy as clojure (sorry) to do it, and this causes them to whinge.

      Some combination of those two. I’m sure the second one is purely hypothetical since no one ever whinges on the internet.

    • Uehreka2y

      > Why such an emphasis on what language stuff is written in?

      Because people are interested in the way that languages affect your ability to think and solve problems? Because different languages have different tradeoffs and the tradeoffs involved in less used languages are often underexplored? Jeez dog, ease up.

    • lemper2y

      because it's not rust, bro! if it's rust, people will accept it and move along.

      aphyr should've written it in ada, though.

  • tieway592y

    [flagged]

    • koudelka2y

      Honestly though, bless Kyle for truly being himself on the internet.

    • jrflowers2y

      This is a good point. We should draw more attention to his social media account because maybe people might read this without taking the time to click through

  • asylteltine2y

    [flagged]

    • roughly2y

      Well then have I got the article for you

    • tmtvl2y

      While I agree that square brackets are abominable, beauty is in the eye of the beholder. I'm sure that Clojure users find them a more elegant solution than the classic number sign followed by round brackets like Lisps use.

      And hey, something like Rust code can be quite ugly at times but you can hardly deny its efficiency.

    • finnjohnsen22y

      Only to the untrained eye. Once you learn it, it is beautiful. It is the simplest language you’ll ever learn, as has very very little syntax.

  • metadat2y

    > Jepsen’s (gosh) about a decade old now

    I thought Jepsen is more like 12 years old now, going by when I first heard about it circa 2011.

    • tomnipotent2y

      The song that inspired the name was released in 2011. Aphyr's "Call Me Maybe" blog series/talk started in 2013, and the commercial audits we've come to love came later.

    • dllthomas2y

      From a certain perspective, Jepsen is 38 years old.

    • michaelsbradley2y

      less than 15, “about” can go +/-

  • snoopsnopp2y

    [flagged]