20 ms·
Why Clojure? (2018)
- non-e-moose 6y agoI'm not sure I see the benefits here unless you are buying completely in to: Emacs, and Java and ignoring performance/overhead. Closure is implemented in Java; and the only apparent way to write it is via Emacs. The zero-eth issue I see: functional programming espouses absolutely no side effects, meaning no capability of handling network of physical I/O errors The implementation being in Java means that it is not possible to use it in embedded environments (which might not be a problem for some users) but it does mean that performance is JVM limited. Personally, I find the requirement of Emacs to be QUITE odious. In my opinion, Emacs is an OS/environment and not an editor. I'll use it if I want to edit a binary, but when an editor includes a psychotherapist mode (Eliza) it is not suitable for software development. I have also seen some quite talented researchers/engineers spend multiple seconds trying to remember the sequence to do X in Emacs, when it would have taken FAR less time in vi or vim. Too many parentheses make the code UNMAINTAINABLE in a production environment.
- newtwilly 6y agoThere are other IDEs people like besides emacs.
- pinchhit 6y agoA couple of things here: - Intellij IDEA with the Cursive extension is very popular outside of emacs (I've met more clojure developers who use IDEs than those who don't.) - Clojure uses the error handling mechanisms of the target runtime. You have try/catch statements and side effects are often used. It's not a no-side-effect language. - Parentheses are almost always managed with parinfer/paredit and python-style indentation rules in production code I've seen. You're quite right that performance will be tied to the JVM or V8/SpiderMonkey/etc.
- swayvil 6y agoGratuitous parenthesis nesting. We see it a lot in Clojure. Nesting boxes instead. It could be represented, in the editor, that way. It could be much more legible. Is anybody doing that?
- appleflaxen 6y agothey are logically equivalent (of course) and the parentheses become invisible once you are used to them. Every language has a threshold of "fluency" which, once you reach it, makes it easy to understand at a glance. Retooling to use nesting boxes has obvious appeal to a newcomer, but none to an existing programmer, and the cost to the corpus of existing programmers is far too large to justify.
- bitwize 6y agoWork with a Lisp long enough and you don't even see the parens anymore, you just see blonde, brunette, redhead...
- fulafel 6y agoWe see that a lot in other languages too (taking curly braces as parens too). There have been many attempts at languages that separate (or even do away with) the textual representation vs what is shown and interacted with in integrated tooling. Seemingly it has nice properties like forbidding invalid syntax. So far the common textual representation seems to be important enough to win over the AST languages.
- thom 6y agoEvery time Clojure comes up on HN there always seems to be a lot of negativity. I've come to flinch just seeing a mention of the language, which I do still love and have used for a decade, but perhaps this is how everyone feels about their pet language. I think there's a bit of a tension with Clojure, that it's a weird combination of being principled and being pragmatic, which means that there are always languages that on paper beat it out on any given axis. At the same time Clojure people do have a tendency to paper over the cracks quite a lot, at least some of which must be cognitive dissonance. The biggest problem is that it's very hard to picture why, at the end of the day, all the choices that went into Clojure come together into a productive whole for building real-world software. It's a really nice mix of terseness without preventing clarity, simple lightweight modelling paradigms, interactive development, easy access to multiple cores, and all on top of the JVM with its enormous ecosystem. It's not as Lispy as other Lisps. It's not as pure as other functional languages. It doesn't have a fancy type system. It doesn't have native performance. But it gets stuff done and it does so fairly elegantly in most cases. I've found it a really solid career choice, there's really very little that you can't solve in a satisfying way. Plus whatever you think about parentheses, Clojure syntax basically hasn't changed in 10 years, most new features are just libraries of new functions and macros, and for me that validates that it's the correct approach.
- cageface 6y agoThe problem with lisp is that most programmers find the syntax extremely off putting. I've been watching this debate go around and around in circles for 20 years now and nothing has changed. And as much as I appreciate the power of macros I feel like the features of other modern languages cover a lot of the same ground so the benefits of such a controversial syntax are diminished. Lisp is a fascinating and very elegant language and every developer can learn something from it but I gave up waiting for the lisp revolution a while ago.
- cageface 6y agoThe other problem with Lisp is that it attracts a fanatical element that rejects any criticsm of the language. Every language has its zealots but Lisp takes it a step further. Google Erik Naggum for an extreme example of this syndrome.
- butterisgood 6y agoWhy not Zoidberg
- mnming 6y agoI am a long time Clojure user (since 2012). I am still using it in my personal web projects with a lot enjoyment. However, I wouldn't use it on any projects with more than maybe 3 team members for mainly three reasons: - Very steep learning curve despite the seemly simple syntax. - The community is shrinking with many high profile OSS projects being effectively abandoned, while most other languages' communities are growing. - Clojure is just bringing too much freedom for most average developers. If it's just me, I will have no hesitation and always pick Clojure as my main language.
- deleted 6y ago[deleted]
- deleted 6y ago[deleted]
- mbrodersen 6y agoThanks for being levelheaded in your assessment of Clojure. I am so tired of claims that language X is “better” based on a subjective selection of what “better” means. X might indeed be better for Y (that you care about) but not for Z (that I care about).
- jopython 6y agoMost of the clojure libraries i found were suffering from bit rot. Looks like the community is shrinking.
- macmac 6y agoI would agree with all of these points except "pattern matching". Yes several libraries implement it, but it is not built in and even the best libraries feel clunky compared to for instance the built in destructuring. Rich explicitly rejected pattern matching ala ML for the reasons provided here: https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f#why-no-pattern-matching https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd6...
- nojito 6y agoCalling pattern matching brittle is absolutely hilarious and shows how out of touch he is. F#'s pattern matching is a clear example of it done correct.
- fiddlerwoaroof 6y agoI always found core.match really nice: but, in general, I strongly prefer using multimethods for the sort of thing other languages use pattern-based dispatch for.
- bmitc 6y agoI wish he gave more examples in that response because I'm generally confused what he's talking about. I'm familiar with Racket and F#, but not having used Clojure, I'm missing some context about the Clojure ways of doing things and examples of the problems he claims. > I feel about them the way I do about switch statements - they're brittle and inextensible. That is not the case in a language like F# or OCaml. I do note that F# was introduced only slightly before Clojure was, but pattern matching provides nicely extensible functions and are anything but brittle in those languages. Also, active patterns in F# allow one to extend the pattern matching functionality. > The binding aspect is used to rename structure components because the language throws the names away, presuming the types are enough. Again, redundantly, all over the app, with plenty of opportunity for error. I'm not sure what he means here. Again in a language like F#, names of the data aren't thrown away. They are pattern matched against, only being "thrown away" to do actual calculations. Nothing is ever lost where the data came from. For example: type Shape = | Circle r | Square s let area shape = match shape with | Circle r -> System.Math.PI * r * r | Square s -> s * s There's no confusion here. In fact, pattern matching in a language like F# allows one to completely remove the possibility of error. For example, this really shows off in parsing applications. Once your parsing function returns a type that can be pattern matched, it's extremely difficult to have an error in the pattern matching sections of code. These are typically the most robust parts of the application. > I'd much rather use (defrecord Color [r g b a]) and end up with maps with named parts than do color of real*real*real*real, > and have to rename the parts in patterns matches everywhere (was it rgba or argb?) I don't understand this either. In F#: type Color = { R: float; G: float; B: float } let colorFunction { R=r; G=g; B=b } = r * g * b No names are thrown away. Also, the comment on rather using maps seems to assume the data type for every element of the data structure is the same. How do you just use maps when the underlying types of your record aren't the same?
- johnday 6y agoNow I'm somewhat biased, but that complete list of advantages also applies to Haskell. [^1] In fact, nearly all of the claims made about Clojure here can be made about haskell more strongly. I've half a mind to do a direct comparison on every point. I'd be interested to hear the author's thoughts on the similarities and differences. [^1] With the obvious exceptions of s-expressions, java/js interop, and "subjectively good design".
- cdmckay 6y agoThe JVM interop is a huge positive for Clojure, in my opinion. Being able to consume any JVM library makes Clojure usable in many more professional settings than Haskell.
- fiddlerwoaroof 6y agoYeah, I worked at a company where several Haskell projects crashed and burned because of interop issues with existing JVM systems, but the Clojure project I worked on did really well.
- johnday 6y agoYeah, that makes sense. If the killer app is JVM interop, nothing but JVM based languages should even be on the table. The "why Clojure?" question just becomes "because Clojure is the best language on the JVM," which is not too interesting of a topic IMO. Haskell has decent interop with C/C++ languages, but certainly nobody uses haskell because of that.
- BoiledCabbage 6y agoIf the killer app is practical usage than Clojure clearly comes out on top. The problem with function language adoption is people keep pushing Haskell likes it's anything more than at its essense a language exploration research project. The fact that you'd have a comment that ignores a language because it wins by default because it practical is more proof that people evaluating languages are speaking two different... well languages. Some are looking for what they feel have the coolest ideas, others are looking for languages with very cool ideas, much better than what they're using today, and can still be effective/ productive in. Haskell is cool if you want to think about or play with ideas. Clojure is cool ideas, and can still be productive in (ie full modern library support). As well if you don't know, F# falls into that same bucked of cool ideas, better than your avg imperative language and can still be very productive due to language and .net library. We're trending to a point where any non-systems language is just an exploratory language unless it's build on JVM or .Net. Otherwise the produvtivity loss from lack of libraries is almost impossible to overcome from any possible produvtivity gains from the language.
- lxtx 6y agoLanguage intricacies aside, is there a reason to use Clojure over Elixir, Erlang? Genuinely curious what JVM has to offer vs BEAM / OTP if you're going to use dynamic languages.
- thu2111 6y agoIt has really good monitoring, profiling, interop tooling. Both JITCs it has (C2 and Graal) are very good at and optimised for compiling dynamic languages. Also the recent JVMs have GCs that can collect enormous heaps without stuttering of any kind.
- jwr 6y agoPracticality. Most of language "comparison" discussions miss out on the practicality aspect: does it work, does it have a good runtime (both true for Elixir and Erlang), can you write code that runs both client-side and server-side, are there good abstractions and libraries for many programming models, is it being actively maintained and developed? Clojure ticks all of those and more, while most superficial comparisons concentrate on superficial aspects.
- cdmckay 6y agoThe JVM ecosystem is many orders of magnitude larger than than BEAM/OTP. For example, you can usually find an API library for Java even from small vendors. I don’t think I’ve seen any vendor provide an Erlang API library.
- macintux 6y agoBasho did for Riak, but that might be due to the fact that Riak was written in Erlang...
- bcrosby95 6y agoClojure's version of immutability is more useful in some domains than Elixir/Erlang's. E.g. you can both safely and efficiently share memory in Clojure across multiple threads. You can't really do the same in Elixir that I'm aware of - it triggers a deep copy which can kill performance. Sometimes acceptable, sometimes not. Elixir/Erlang processes serve a lot of roles. If those roles don't line up cleanly your code could end up a lot more complex than necessary in other languages. In the past the JVM had better raw performance, but I'm not sure how much that might change with the new JIT in BEAM.
- city41 6y agoTo not go absolutely insane with Lisps, you need some kind of parentheses tool with your editor such as ParEdit. The up side is once you get used to your tool, you can do really cool things and navigate/change code in higher level ways. But the massive downside (in my experience), is convincing coworkers to adopt a Lisp is practically impossible. The language itself being so foreign, and when they ask about the parentheses bringing up a tool they need to learn on top of the language is a double whammy.
- deleted 6y ago[deleted]
- jwr 6y agoWhile I would agree that paredit in Emacs is fantastic, the real shock comes when you have to go back to editing code in those languages that have the weird arbitrary punctuation. I mean, your editor can't even properly manipulate those expressions most of the time. In order not to go absolutely insane when dealing with JavaScript, C++, Java, you need absolutely top-notch editor support, and even then you can't do everything that paredit does. This gets even worse with languages where indentation matters (Python, and the horrible abomination that is YAML) — which aren't even auto-indentable, because the editor has no idea what you actually mean. I'm not sure if you can avoid going insane with those.
- mbrodersen 6y agoI am a bit puzzled reading this. I have programmed in C++ for 20+ years and (as far as I know) am still fairly sane. I also have never met or heard about anybody programming in C++ who lost their mind programming C++ ;)
- jb1991 6y agoThis is a common argument, but I think your mileage may vary. I had a Clojure job for many years, and it was the only thing I wrote in that time. When I moved to a different job in a more conventional language, I also had that initial frustration from the syntax context switch in my head. But it was very short-lived. Now I find that there’s really no such frustration, it’s just a different way of writing code, going from either direction to the other requires some getting used to it.
- BossingAround 6y agoHow about Clojure vs Scala? Anecdotally speaking, I've seen more Clojure than Scala at my company, both being incredibly niche (I've seen more Groovy than either to be honest). If I want to get more into FP, is there any strong positives/negatives for either? I must say though that after using Racket for a bit, I am a fan of the parens. Makes expressions crystal clear.
- yw3410 6y agoI use a variety of languages and always feel that Scala gets a bad-rap because it's a compromise language (though I feel the same about Rust and most people seem to like it). Scala 3 pros: + Higher-Kinds + Type-classes + Decent-ish hygienic macros + Decent OO model with mixins + for-comphrension + Compile-time evaluation + Laziness + Currying It's pretty good at all of them - but not quite. If you're used to Haskell you'll get annoyed at the fact that the inference is only within a single statement and that polymorphic functions resolve the typeclasses in a particular way. If you're used to Scheme, you'll get frustrated that the identifiers can't be created easily in its new macro system or you can't manipulate the scope-sets of the identifiers. If you're used to Erlang you'll wonder why Akka can't reload functions easily. If you're from Java you'll wonder why everyone is going on about profunctors, effects and finally tag-less.
- kodon 6y agoScala seems to get the most love from spark users. But even then the python bindings are pretty good. Scala 3 is going to be released soon, so there might be a surge of interest.
- schmooser 6y agoSeveral big mainstream products are implemented in Scala, including Apache Spark and Akka. Clojure has nothing of comparable size.
- fshr 6y agoMetabase is written in Clojure.
- 6y ago
- draw_down 6y agoI no longer think language matters much. I like some more than others but they’re all a bag of hurt, you just get to pick which kind of hurt. I agree that pure functions are nice. But things like this end up mattering a lot less day-to-day than dumb-guy practical stuff like packaging and tooling. The important thing about clojure is that it runs on the jvm.
- deleted 6y ago[deleted]
- sukh 6y agoThe article and examples were lucid and easy to read. Thank you!
- darksaints 6y agoI always think it's hilarious when Clojure enthusiasts try to address concerns about the language by talking about parentheses, as if that was actually the major barrier to entry. The parentheses are at best a mild inconvenience...many people love them, including myself, but few people cite the parentheses as a reason to not use the language after actually trying it out. A non-exhaustive list on why not clojure: * It's slow * Development with the REPL is slow because the startup times are glacial and REPL-oriented development usually requires tons of from-scratch restarts. * The tooling sucks: build systems, IDEs, debuggers, etc. If you feel like writing code with just an editor and a terminal is like going back to the stone ages, you're gonna want to bash your own head in with a mammoth bone club. * Java interop is a black art, and when you need to use it, it will ruin any sense of elegance you felt for your code originally. * The ecosystem practically doesn't exist, unless you're willing to absorb a lot of java libraries. See above. * The lack of static types hurts you in many ways, most of all your ability to refactor with confidence. * Clojurescript isn't the same language, no matter what they promise you. Clojurescript is weakly typed, Clojure is strongly typed. If you aren't aware of the difference, prepare to spend weeks of your life tracking down bugs that would only be days in Clojure, and would never exist in the first place in a statically/strongly typed language.
- raspasov 6y ago* It's fast enough for 99% of apps out of the box. It's fast enough for 99.99% of the apps with minimal tuning. * Yes, if your project is very big and macro heavy, it can take some time, but startup times have improved. In any case, I BARELY need to restart my development JVM. I have one currently running that I haven't restarted for 1 week+. * Depending on what's your cup of tea, there's emacs/CIDER or IntelliJ/Cursive. They both work well. IntelliJ/Cursive is an excellent IDE combination. I use it every day. * Java interop is very straightforward, not sure what you mean. Sure your code might not be all pure anymore, but that's the price for solving actual problems. * Good java libraries have wrappers. A ton of original Clojure libraries as well. https://github.com/cgrand/xforms https://github.com/cgrand/xforms for example allows you to easily do things that I can't even imagine doing in an imperative language. * Static vs dynamic typing: don't want to get into that. * "Clojurescript isn't the same language". I use both Clojure and ClojureScript every day and as far as Clojure-only code is concerned, it works in both languages 99.99% of the time. One case you can encounter issues is if you do something host-specific, like dealing with numbers. That's by design. Clojure embraces each host, does not try to reinvent it. When you just use pure Clojure data structure manipulation, it works the same across both languages and works like magic.
- fmakunbound 6y agoI was using Clojure for a while before 2018, and the error messages were brutal. Has that improved since then?
- thom 6y agoThey've improved over the years, but they're still terrible compared to something like Rust. There are more ways to sidestep the issue though, if you develop with Spec and generative tests etc.
- robertfw 6y agoThe error messages were what got me to stop using Clojure when I first tried it out several years ago, the revamped error messages are what helped me to stick with it to the point of building out a complex production system. Dramatic improvement.
- elwell 6y agoNot an answer, but... I do think that after you've used Clojure for a while, you get used to the Java stacktraces. I mean it has the line number of the error, it can't be that bad, right? Maybe I've never experienced a language with stellar error messages.
- dgb23 6y agoI’d say this is still the weakest part.
- capableweb 6y agoDefinitely. If you're using Clojure on the JVM, you definitly should read up on Java exceptions even if the errors have improved already, as it'll help you debug things, but the default error messages in Clojure has improved since then. Same if you're dealing with ClojureScript, you'll need to understand JavaScript errors and stack traces then. Probably a good starting point for people today is go straight for Babashka, easy to get started and fast: https://github.com/babashka/babashka https://github.com/babashka/babashka
- FredrikMeyer 6y agoYes, they got a lot better in Clojure 1.9.
- bernardv 6y agoThis laundry list of features still does not tell me why I should be using clojure over any other language.
- lbj 6y agoWell, thats true but its implied. The entire syntax for Clojure fits in a single line. Its easier to learn and being as expressive as it is, the core idioms are quickly picked up as well. So - You can get into it quickly, very quickly if you're already familiar with FP. The brevity of the code means that you'll produce much more robust code, which takes up a lot less screen real-estate. This allows you to grasp the functionality of any code you read, very quickly and start working on the problem. It'll go as fast as Java, but slower than C/Rust. For some performance oriented tasks, you'll have to put in more work than makes sense, to get the performance you want. But for 99% of the Apps that are being written, Clojure will perform just fine and you'll end up with better code. Compared to Haskell or most other FPs (not F#) you get the added benefit of being on the JVM. Write once, run everywhere. Huge libs to do everything from 3d graphics to webserving. In most cases, I use Clojure for the above reasons summed up in this one sentence: I makes me more effective than the alternative. ps: Having enjoyed Lisps for 20 years or so, Ive never used Par-Edit :)
- cutler 6y agoClojure is nowhere near the speed of Java. That functional abstraction layer has to be paid for.
- p0nce 6y ago> The entire syntax for Clojure fits in a single line. But some of us absolutely don't like this syntax. The small bits of Java in the article are very readable in comparison.
- jhomedall 6y agoNobody likes Lisp syntax initially. That goes away after working with it for a short while.
- x87678r 6y agoHas anyone seen a big - multi year system done in FP? Lots of people love FP, it seems great for your own side project, but I'm not convinced it works in those typical big corporate systems where devs turn over every few years as the code base grows.
- JackMorgan 6y agoI've worked in big F# projects in banking. It's absolutely superior to C# or Java in many ways. One notable drawback is "how much code can new hires write in their first month" which is not a metric I consider that important for big enough projects. The month or so needed to skill up a C# or Java programmer in F# is a drop in the bucket compared to the benefits it brought us.
- gidan 6y agoPitch (from Berlin) is using Clojure as well as Reagent with React. From what I saw it's quite a moderately sized app (not a gigantic one, but not a side-project sized either, in between that's what I mean).
- beders 6y agoIt's working fine for us and we just hired 6 new clojure devs. Code base is quite significant yet easy to make meaningful changes to. Most reasoning is local to your changes thanks to the focus on small, pure fns dealing with immutable data.
- adamkl 6y agoNubank seems to be doing just fine: “From [its] start in 2013, Nubank has grown to 600 Clojure developers, running 2.5 million lines of Clojure code in 500 microservices”[0] [0] https://www.fintechfutures.com/2020/07/brazilian-challenger-nubank-acquires-firm-behind-clojure-and-datomic/ https://www.fintechfutures.com/2020/07/brazilian-challenger-...
- Scarbutt 6y agoTo be fair, they had to buy Cognitect to cope with the technical debt of Clojure and Datomic.
- tkdc926 6y ago> In Clojure, whenever you "append" to a vector (array) you get a "new" vector and the original does not change. Anyone with a reference to the original can always count on it being the same. This has never made any sense to me. Can someone please explain why you would still want the original vector to continue to exist with data that no longer reflects the current system? What am I missing?
- MichaelMoser123 6y agowhen you work in a functional programming style then every modification of a objects data member would create a new copy of the object; that one is mostly a copy of the old object except for the variable change that was introduced by the setter. In general that make a lot of sense for smaller objects (in Java the String is immutable, so are tuples) - it is easier to reason about the object and you don't have race conditions. in Scala you have mutable collections and immutable collections - like that one; the more accessible versions that you have in your default namespace are immutable (that's supposed to be the default choice). now in theory smaller contingent objects that span 'a few' cache lines would be easier to copy than to modify. Now my problem with that statement is that with the JDK you usually have lots of Object references (can't do a lot with primitive types), so you need to try hard in order to get an object that spans a few cache lines. You would have more of these in go, but they don't do a lot of functional style programming in go, afaik. maybe it would make some sence to port clojure or scala to the golang runtime.
- cutler 6y agoIn a word - concurrency.
- jsiepkes 6y agoImmutability is very useful for dealing with concurrency. For example if a thread is iterating over a vector and an other thread mutates it you don't want the first thread to get "the rug pulled from under it" so to say. If things can never be suddenly changed, you don't have to plan for that.
- fbn79 6y agoSee this answer https://stackoverflow.com/questions/34385243/why-is-immutability-so-important-or-needed-in-javascript https://stackoverflow.com/questions/34385243/why-is-immutabi...