Hacker News new | past | comments | ask | show | jobs | submit login
Why I'm Productive in Clojure (2013) (yogthos.net)
182 points by tosh on Oct 22, 2017 | hide | past | favorite | 246 comments

I've really taken a shine to Clojure. Parentheses and dynamic typing initially kept me away, but they're far from the thorns they've been made out to be.

Parentheses? You don't even see them after a short while--as a beginner, you use something like parinfer, you are good to go. That, and like all lisps, you begin to appreciate programming directly in the abstract syntax tree: the language you type and the way it is interpreted aren't disjoint things.

Dynamic typing? Being functional, immutable by default, and data orientated steal the vast majority of the sting from that in my opinion--and leave you with often downplayed advantages. On the last point, "data oriented", to put it succinctly, data itself is first class or the prominent currency among Clojurists, rather than a zoo of abstractions that never quite fit and and all have to be reconciled and made to play with one another (and often the reason you need types everywhere to keep it all straight).

But where types--or validation--are more crucial near the boundaries of the API, there are outside the box solutions like Spec that don't rely on a baked in type system.

The recent keynote by Rich Hickey covers a lot of these points (and far better than me):


If you have used a statically typed language in the ML family (OCaml, Haskell etc.), could you contrast the development experience between the two?

I spent a good year or so with Haskell. I guess at first blush, one thing that jumped out was that Clojure was made from the jump with a focus on productivity and pragmatism, whereas you never feel too far from the academic origins of Haskell. Clojure is a very shrewdly designed--it's not proud, it takes leverage where it can find it, and makes some very deliberate and sensible trade offs. A large aspect of that is the JVM it sits atop of. You will know that it's Java underneath the hood, but this is not a bad thing at all. You get to have your cake and eat it too. It's done really well in the sense that while it would be awkward and not Clojure-like to promiscuously call native Java--especially code with side-effects--it it is perfectly alright, and practical, to pull in any of the multitude of proven Java libraries where it can obviously save you time and effort. There is no snobbery in the core language itself or in the community about "ew, icky Java". Or put another way, there is no from-scratch ecosystem like in so many languages where a new language means starting over (so the language needs a crypto library, and a web server, and...) Not to mention, many of the utility libraries out there are largely functional in nature anyhow (you can use a massive chunk of the wealth that is Apache Commons as just an arsenal of pure functions).

In nitty gritty terms, programming in Clojure actually feels a lot like programming in Haskell to me, without the heavy type system. Lazy infinite lists, all your FP classics like map, reduce, filter, etc. work exactly like you would expect. But I don't miss having to create a stack of monad transformers in an area of code where I need to mix IO and state or something...and using type synonyms to mask the monstrous Turing-complete type signatures I had to make to describe it... In Clojure, you just put the "dirty" work in its own function, clearly separated from the pure functions (which are the de facto default), and you push them to the boundaries of the system while passing extracted data into pure functions (just like Haskell). If you did anything different, you'd get the stink eye from even the least principled Clojurists. It's just not done. A ball and chain type system is unnecessary.

The lack of (explicit) types has been at the very most a minor nuisance. Occasionally I mistype something or miss a parameter, you're going to see the error pretty quickly in the REPL. If you run or test your code at all, the pedestrian type errors (oh, that was a string not an int) will be wrung out immediately. That benefit of type systems (preventing typos, essentially) has been much, much overstated IMO. Given you're going to be in the REPL a lot with Clojure, you'll be iteratatively developing and running your code constantly, so it's really not an issue. As far as refactoring, I think that's where there is more meaningful benefit. I use Intellij/Cursive IDE, and the refactoring capabilities are good--much like if it were a Java project (and there are hints and such for the typos I mentioned earlier, autocomplete, etc). So, while it is more of a challenge for IDEs, there are some excellent ones that do a pretty bang up job of inferring types for you.

I haven't used Clojure on the job (Java dev by day), but I would have no problem using it for something "real", whereas I never felt anywhere near confident in using Haskell for something outside my own fiddling.

Thanks for the reply, it was very informative.

I've programmed in dynamic languages for most of my career (about a decade now), and have always missed types. I picked up OCaml (Reason flavoured) recently, and I've been able to enjoy the Hindley-Milner typesystem without worrying about side-effects with complex types, since OCaml is a fine mix of imperative and functional programming.

Language preferences are usually framed as an objective measurement, but I've always found it to be based on our personal experiences. Since you've been doing a lot of Java, I'd surmise that the clean, functional yet dynamic nature of Clojure with Java interop is appealing to you. Me on the other hand has been burnt enough times with dynamic systems that keep shifting from under your foot (Ruby metaprogramming), test suites that makes refactoring more difficult (it provides safety, but the manual labour is unforgiving), and a general sense of dread everytime I put systems into production. But more than anything else, the biggest pain has been the sheer difficulty to refactor growing codebases.

Large dynamic systems, if they manage to not crumble from the inside, usually gets hammered by a sweeping change in external reality (terminology changes, shifting of levels in domain hierarchy etc.). When there is a pressure to ship we add a new mapping without touching existing naming, and slowly the domain names in the system exhibits a tangled relation with the actual domain.

That's just been my experience, and so I'm here learning me some good old OCaml and being very happy with what I'm getting out of it.

Have a look at Spec and the design philosophy behind it if you are still interested in Clojure at all. Especially how Hickey feels about open specs, versioning, and names. It is a pretty opinionated stance to take on solving those issues, but it convinced me and I no longer have the worries you describe about the dynamic nature of a large Clojure system.

I would love to use closure, i just stay away from dynamically typed languages - after having a bad experience trying to maintain projects written in a dynamically typed languages which was an awful experience. You see a parameter named user and you have no idea which type it is, which fields it has, does this user has a phone number? how can i know? You see functions and you don't have any idea what is the type they return. just to put things in perspective I maintained X100 larger projects in a statically typed languages, with even worse code but maintaining it was easier than these other projects. Simply because I knew what functions were getting in and what their return type was.

Clojure has `defrecord`, so you can `defrecord User`.

Then you can assert that `user` is a User, with type hints, preconditions, 'spec' (a Clojure library), tests, docstrings, or some ad-hoc convention ("this kind of namespace does this kind of data transformation").

Then again, some Clojure users might prefer to use plain hashmaps (Rich tends to encourage this; or at least that's the common interpretation from his talks), and none of the mentioned mechanisms.

The mechanisms are there. Up to each to use them or not, depending on your scenario (and sincerely on one's competence too)

I generally agree with you, but Clojure is not at all like other dynamic languages (like, say, JS). For one, Clojure's dynamic dispatch is more like C++/Java's -- i.e., when you call a function, you can statically know either the precise target or an explicitly designated set of possible targets. Second, Clojure allows you to specify the types of arguments (and much more, in fact), not with a type system, but with a specification system. It's not exactly the same, but it can be more/less powerful than types, depending on usage.

How is the specification checked, statically or dynamically?

You can see my other comment to your same question. Also, even if you do not use Spec, you do get a lot of compile-time validation like making sure all arguments are passed to a function, not leaving them out, etc. Try that in Javascript!

Are you sure about "you do get compile-time validation like making sure all arguments are passed to a function"?

If I run this:

    (defn bar [x y]
        (println x y))

    (defn foo [v]
        (if v (bar 12 42 79)))

    (foo false)
The program prints nothing and Clojure reports no error.

But when I run this:

    (foo true)
Then Clojure reports an error:

    Exception in thread "main" clojure.lang.ArityException: Wrong number of args (3) passed to: user/bar, compiling:(.../test.clj:6:5)

I don't spend much time on the JVM, but in ClojureScript, this is what I get for a similar test (including multiple definitions of foo in my case, i've elided the whole source file here):

     (defn foo [a b] :hi)
     (defn bar [] (foo 1 2 3))

     WARNING: Wrong number of args (3) passed to prepost.core/foo at line 25 src/cljs/prepost/core.cljs
     WARNING: foo at line 24 is being replaced at line 48 src/cljs/prepost/core.cljs
It's a compile-time warning even when the app is not running, but ClojureScript goes through a different compilation step than JVM Clojure. Perhaps I was mistaken in the JVM case, or someone else can chime in.

Maybe ClojureScript is doing something Clojure doesn't at compilation time…

I would encourage you take a look at the rational behind Clojure spec and Schema. Both allow you to specify the expected types of the arguments.

How is the spec checked, statically or dynamically?

It can actually be used in a variety of ways. It can be a runtime assertion. It can also be used to automatically generated unlimited tests (using property-based testing, similarly to Haskell and other languages) and that can be done at compile time. That is, you write a spec for a function and the tests are automatically created to ensure that all valid input the function can handle matches corresponding output it emits.

Because these are not actual static types, you can get a lot of control and say things like "this function accepts three arguments, all integers between 5 and 50, but in increasing order, and the output must be a float between 0 and 1", and then have that checked on the function before the program ever runs. At run time, you can then verify that you are passing in valid arguments.

Spec is relatively new, and there are experiments in the community to do things more like traditional static type checking also. The future is an interesting place.

You wrote "then have that checked on the function before the program ever runs". That is the definition of static checking.

To statically check constraints like "all integers between 5 and 50", you need dependent types, and Clojure doesn't support this.

Considering this, I'm not sure what you mean by "before the program ever runs". Do you mean a check thanks to automated property-based testing (which is a way of running a part of your program)?

No, I'm not talking about static checking. I'm talking about property-based tests that run at compile time. Dependent typing is a different matter. What I describe is not dependent typing, but is quite straightforward now thanks to Clojure.spec. And there are a lot of community efforts in the research sphere to see where Spec can take static analysis, it's quite interesting.


Dynamically, but there are some libraries out there that are working to bring static analysis on steroids to spec-enabled code. And as spec is opt-in, you can spec just how much (or little) you find useful.

Interesting. Any link?

Sure: https://github.com/arohner/spectrum

I think the main advantage of a type system (e.g. Java's) is that it avoids a lot of stupid mistakes; and therefore does help refactoring because most "stupid" changes will simply not compile (rename a function but forget it's used in 3 other places? boom)

Static analysis with Spec might be a great booster because it gets you the expressiveness of Spec to static code reasoning.

Thanks for the link.

I've used static and dynamic languages. When using a dynamic language, I usually don't miss static language features, except when refactoring and/or maintaining a large code base. It's the reason why I'm interested by Flow and TypeScript (in the JS ecosystem), mypy (in the Python ecosystem) and even static languages like Go. It's also the reason why I'm a bit reluctant to invest in Clojure, despite being interested by the language.


Which language?

mostly javascript, but I had a very similar experience in ruby and python. The main challenge was maintaining projects developed by other people.

I worked exclusively with statically typed languages for about a decade before moving to Clojure. I've been using it for about 7 years now professionally, and haven't found dynamic typing to be a problem. If it was I would've gone back to using a static language a long time ago.

I think dynamic typing is a lot more problematic in imperative/OO languages. One problem is that the data is mutable, and you pass things around by reference. Even if you knew the shape of the data originally, there's no way to tell whether it's been changed elsewhere via side effects. The other problem is that OO encourages proliferation of types in your code. Keeping track of that quickly gets out of hand.

Clojure embraces immutability, and any changes to the data happen explicitly in a known context. This makes it much easier to know exactly what the shape of the data is at any one place in your application.

Meanwhile, all the data is structured using a set of common data structures. Any iterator function such as `map`, `filter`, or `reduce` can iterate any data structure, and it's completely agnostic regarding the concrete types. The code that cares about the types is passed in as a parameter. Pretty much any data transformations in Clojure are accomplished by chaining functions from the standard library together, with domain specific code bubbling up to a shallow layer at the top.

There's also the culture of using Spec and Schema in Clojure, so you often have assertions at the interface level. For example, there's ring-spec https://github.com/ring-clojure/ring-spec/blob/master/src/ri... providing the specification for Ring.

Hi, I went on and searched for a random clojure project in github, and have chosen some function which is of a small size:

(defn parse-seq [parse-el form] (when (sequential? form) (reduce #(if-let [parsed (parse-el %2)] (conj %1 parsed) (reduced nil)) [] form)))

without knowing really clojure i take it "[parse-el form]" are the arguments of this function. How is it possible with minimal effort to know what am I supposed to pass as parse-el and form? what are they?


Let's try to look at it in isolation.

"parse-el" is used as a function, which takes one argument, the first argument when invoked from reduce (the accumulator), which is first [] then another vector (because of conj).

So "parse-seq" parses a sequence (named "form") and produces a vector, or nil. It applies "parse-el" to each form using reduce. When the result of any form gives a nil value, the whole result is also nil (see https://clojuredocs.org/clojure.core/reduced). The result is also nil if form is not sequential.

There is no other contract about "parse-el", it just should be able to "parse" whatever occurs in "form". You could use it on your own with any kind of data that has a corresponding parse function.

A documentation string would have been nice, or different names ("parse-el"?)

Now, if you look at the usage of "parse-seq", you can see for example:

    (let [vars* (parse-seq parse-variable vars)
And it looks like "parse-variable" parses a form and returns an instance of Variable.

    (defn parse-variable [form]
      (when (and (symbol? form)
                 (= (first (name form)) \?))
        (Variable. form)))

"the first argument" => "the second argument"

This is a very interesting question. To me, understanding in isolation some random function deep down in the code somewhere is not really something I do. Maybe that's why I prefer dynamic languages. And other people like to be able to do that, and therefore prefers static languages.

This has definitely been a challenge for me in Clojure when trying to use a library that isn't well-documented. Using the REPL helps because I can throw random things at it quickly, but it's still very frustrating.

An interesting corollary to this problem though is the wandering type-path, where when consuming (again, poorly documented) a Java library, I know the type of the function I want to call, but not how to get that type. Usually it's:

"I have B value [byte-array, int, whatever] and I need Z from this library so I can call `Z.foo`... Z's constructor takes Y, which takes X, which takes..."

Types help there, but they also hinder quite a bit if I have the correct underlying data type, but can't get Java to accept it.

Look at the function doc string. Look at the function spec/fdef Look at the function comments.

OK, there aren't any. Pull requests accepted?

>mostly javascript

Well, javascript does not have some of the features that other dynamic programming languages have, that help you manage complexity and create successful projects of large size.

Like what?

Strong typing, powerful package and namespacing facilities, a good object oriented system (if you want to do OOP, that is), interactive development with a REPL that can tell you easily which function calls which others, etc.

What's wrong with Javascript's module and OOP systems?

AFAIK JS doesn't really have modules, just ad-hoc use of functions and objects for namespacing

I love Haskell for its type system, but I gotta say that I feel I accomplish about 10x as much with Clojure than I do with it. Being able to quickly beam over my code from Vim to the REPL, and seeing my changes happen in real-time is so incredibly valuable, and I personally feel that Clojure's concurrency is a lot easier to grok than Haskell's.

I also feel that Clojure's tooling is a lot nicer than most languages, and especially Haskell; Leiningen is very easy to use and Fireplace is a really simple and nice plugin for Vim.

When I have to write something computationally complex in the language my employer wants me to write it in, I first write it in Clojure because it's easier to think about the problem in that language. Then I translate that.

I'm willing to bet that any dev who has learned Clojure does the same thing. If I'm right, that's a huge indictment of what we've been doing for the last 60 years in programming.

You'd lose that bet. I think it's easier to reason about problems in languages you are familiar with and that fit how you think, which can change through continued use. A lot of the other stuff is just post hoc rationalization about why we like these languages and why they are better.

The opposite of my experience. I have many more years of experience and am far more familiar with in the target language than Clojure. But for the really difficult computational problems, say, for example, particular performance analysis of a sensor network for a given time series, I reach for Clojure.

Same. Clojure is very, very good at expressing data and data transformations in a simple, concise way. Once I learned to reason in Clojure, it quickly became the language I prefer for playing with a fuzzy problem.

Surely the argument being made here is for the best prototyping language, not the most familiar. If familiarity was the only yardstick nothing beyond C would even exist.

The argument being made is if it's necessarily efficient to prototype in a different language than the one that will be actually used, especially if one is more familiar with the target language.

In a world of options where an experienced programmer has had a chance to experiment with different languages the most familiar language is the best prototyping language. In a world where C is the only option programmers think about what other options they could imagine.

I haven't tried closure but I can believe so. I observe the same phenomenon with Python. Whatever I have to do is just easier to first express in Python then translate for me. I guess there are just some languages that are closer than others to the way we think.

It's quite probable that this language changes from dev to dev, depending of habits, type of problems and skills with said language. However I suspect some languages are just better at it by nature as well.

I know Clojure and have used it for some projects, and I don't do that. If I need to sketch an algorithm, I'll do it in pseudocode, and if I need to test the design I do it in a verifiable pseudocode, like PlusCal. For sketching/testing numerical algorithms, I use Matlab/Julia.

Why does the language matter when reasoning about the problem?

It seems to me you're conflating "how to solve this problem" with "how to use a particular tool to implement a particular solution to a given problem."

I’ve already seen large scale clojure projects being rewritten to something average devs can maintain. Don’t expect much more growth out of this language in terms of new projects written in clojure.

Clojure is great for beating the averages, see: http://www.paulgraham.com/avg.html

Clojure growth is just fine. If average devs have to add something there is Java. In our experience maintenance of Clojure code is orders of magnitude better than average languages (Python, Java, etc). Yes, object orientation in large organizations leads to the most unimaginable mess. People mostly get paid for unproductive behaviour. Clojure is great for startups where you can do things right.

I agree with this. It was this Paul Graham essay that made me use Clojure/Lisp in the first place :)

You might also be interested in the story of SISCOG (http://www.siscog.eu/upload/GESTAO-DE-LISTAS/News/PDF/eclm-2...) - a massive transport infrastructure system designed in Common Lisp. I'm sure Clojure would have been chosen if it was developed today.

Thanks for this post!

>I'm sure Clojure would have been chosen if it was developed today.

Clojure was already available in 2013. The paper shows they made use of the CLOS object-oriented system, which is a key feature of Common Lisp.

2013 is just the publication date. The document cites an implementation of their London Underground system which has been in production since 2008 and a another within the Dutch rail system dating back to 1998.

Ten years ago (appoximately) I read an article about how PHP was dying, thanks in part to many new languages cropping up. But I see PHP mentioned all the time here on HN, and the PHP meetups in my area are frequent and popular, and major companies run on PHP.

For decades, there have been rumors about the death of dynamic languages in general. And yet the decades pass and not only do those languages continue to flourish, but others come along and get popular too.

Clojure just turned 10 years old and I see no signs of it slowing down. They did consolidate their 2 major conferences in the States into just one annual conference, so there's that (which may not mean anything). But I am in a large city and companies keep appearing and starting up with Clojure as their key language.

With Clojure, it's an oddball because it is very simple and very powerful. Oftentimes, simple languages are not so powerful, and powerful languages are not so simple. But with Clojure, you can very easily get yourself into the situation of having extremely dense logic in just a few lines, where another language might easily take 10x (or more) lines to accomplish the same. This leads to code that can indeed be difficult to read later, especially if you are on a team and others must help maintain it. The Clojure community has come up with some excellent solutions to this problem, most recently the Clojure.spec library which is gaining wide adoption, which not only makes such "cleverness" easier to understand when reading it, but also makes a codebase automatically testable with property generators, which is a major feature. But your point about readability is always going to exist with a language like Clojure if it isn't written with a team in mind. That doesn't take away from its power though, in my opinion.

However, there is one particularly indispensable tool that Clojure offers that I have not seen anything to meaningfully compete with for productivity, and that's ClojureScript. Writing a front end with Reagent or even the alternative libraries is such a major win over everything else I've tried. I've worked a bit with Elm, Typescript, Reason, Bucklescript -- I am so spoiled by the extremely fast turnaround and easy interaction with very powerful features like React in Clojurescript that I don't see anything on the horizon that will come close to replacing that workflow for now. Perhaps some day something will come along to shine an even brighter light on how front ends can be developed, but for now, ClojureScript remains one of the most significant contributions to the software industry in many, many years.

PHP has a massive following. It was basically the only language for the web 1-2 decades ago, everything had to be written in PHP. Of course, it cannot be extinguished since the existing sites will drag on forever.

Clojure never had much usage on real world projects. It cannot be extinguishes as it never took of.

> It cannot be extinguishes as it never took of.

I'm not sure what you mean by "never took off" but thousands of developers around the world, myself included, have made their living just writing Clojure code for many years. So, it has certainly taken off for them. And there are many, many companies using it all over the world. I live in a town of about half a million people, so not super big, and there are about 12 companies primarily operating on a Clojure codebase.

A drop in the ocean compared to the hundreds of thousands of developers who make a living in PHP, C, java or python. You know, the major languages.

I'm genuinely intrigued. Which town is that?

A lovely place in Western Europe.

Tell that to Amazon, Apple, Boeing, and Walmart. Just a few big companies using Clojure today.

I don’t know about the others, but the Amazon claim is basically useless. It’s like claiming “As seen on NBC” when it was a Sunday 4am mention on a Sioux City affiliate. Maybe one in 500 teams (optimistically!) uses Clojure. Many of the Clojure code bases I saw while there were either deprecated or were functionally zombie services.

Amazon also uses Fortran, Assembly, Awk, Mosel, VBA, K, and Bash, but nobody is going around claiming they have much usage on real world projects.

Amazon was specifically using Clojure for the online checkout system, and the team bragged a few years ago how the system survived black friday without a hickup.

Walmart is using Clojure for their checkouts as well https://www.youtube.com/watch?v=av9Xi6CNqq4

Boeing is using it in the onboard control systems for the 737 Max https://www.youtube.com/watch?v=iUC7noGU1mQ

It's not some obscure microservice that some intern wrote over the weekend, these companies are using the language for critical applications, and the feedback from these companies is positive.

The online checkout system isn’t a single service. I personally worked with the Delivery Promise service for a short time, and that service was composed of 8 different services...and that was just to calculate when we promised arrival to the customer during checkout. As in, they were just a single component of hundreds called during checkout. At the very least, none of those 9 services used Clojure.

If anybody led you to believe that their team did “online checkout”, but didn’t care to explain how small of a cog they were in an extremely complex system composed of hundreds of services, then you were grossly misled.

I linked a talk from Walmart that explains exactly how they're using Clojure. I don't think I'm misrepresenting anything here.

You absolutely are. You name dropped Amazon as some sort of proof that Amazon is used at scale, completely misrepresenting the scope of usage. Simply put, Clojure at Amazon is an insignificant and inconsequential symptom of a massive service oriented architecture with tens of thousands of software developers across four continents making independent decisions on how to write their tiny services.

I think anything that's part of the sales chain can't be dismissed as insignificant. Just because Amazon uses other technologies, that doesn't diminish its importance.

I can appreciate your desire to share the good news about Clojure adoption, but I don't think the language is in need of defense. It's used a lot all over the place, and you shouldn't feel the need to convince others of that. Because even if it weren't used hardly anywhere, it would still be a great tool for those of us who do use it. That it is in fact in use in a lot of places, regardless of its specific role, is just a nice added bonus.

If someone doesn't want to use it for this reason or that, it's their loss not ours. Those who need to find Clojure eventually will, because there is nothing else like it.

The Boeing video says Clojure is used for maintenance systems, not control systems. That’s an important distinction. Maintenance systems can go offline and the aircraft still flies fine. The critical control software is not written in Clojure from what I could tell.

Critical software is rarely written in anything except languages with explicit memory control, usually C++, and usually with very strict guides. There is a famous programming guide for flight control systems in particular that discusses how to properly use C++ for maximum safety during flight. It includes things like never allocating memory after take-off.

You wouldn't want Clojure, Java or any languauge with GC running in a pacemaker either. Those are all also very important distinctions.

The majority of critical software is C, sometimes it's C++ or Ada instead.

C++ actually proved difficult to adopt over C. The language is too difficult and the tooling is too poor. It's too hard to run a static analysis or prove 100% code coverage.

I'm not sure what your evidence is for this, but I find C++ to be used far more widely than C in nearly all the companies I interact with that do critical low level work. And there is indeed plenty of information in the industry about flight control software in particular in C++. It's also very popular for embedded work, because C++ allows you to offload quite a bit of work to compile time that can only be done in C at runtime. And I've attended talks for security systems in C++ specifically because of control over exceptions and how to track unexpected events, which is something security professionals are very interested in.

With regards to tooling, the windows visual studio experience for C++ is widely counted as one of the best development setups in the industry for any language. I know lots of people working in other languages that envy what C++ developers get.

I agree with you that it's not necessary or meaningful to "name drop" companies that use some X technology as an indication of its worth. Languages and tools don't need a massive following by enterprise to be incredible and revolutionary. It's like a really good movie that didn't have a large audience but was nonetheless really awesome for those who did see it. That's a lot like how I think Clojure is.

PHP, while very popular, has never been the only language for the web. A decade ago it was competing with Ruby on Rails, Django and Java. Two decades ago Perl was the dominant web development language.

I second the statement about ClojureScript. It makes developing in Javascript so much nicer and cleaner. With so much work these days being "full-stack", the combination of Clojure/Clojurescript is Clojure's killer feature for me.

Eh, I don't use Clojure, but I don't think that a lack of a high growth rate is that big a deal. By my unscientific estimate, languages only need about 20,000 active OSS contributors to have a healthy OSS ecosystem, and that's easier to achieve than ever given the growth in the absolute number of programmers. As long as the growth of the language outpaces the attrition rate, it can thrive just the same.

I've never heard this expressed, but it makes a lot of sense. Wonder what that actual number would be and the best way to measure it?

My estimate is based on CPAN, which, in the early 2000s, was already lauded for offering an extensive, high-quality ecosystem despite having "just" 20,000 packages (contrast modern NPM, which claims >500k). Hence I consider this the lower bound of an ecosystem which can be reasonably expected to provide foundations, if not pre-made solutions, for anything you might expect to do in the course of general purpose programming. And it seems reasonable to assume that one maintainer per package is enough to sustain such a system.

Clojure hit 20,000 recently according to clojars.org . So that's it - we're where Perl was in 2001. Makes me feel all fuzzy inside as Perl was my first love.

What exactly do average devs struggle with?

Who is making these decisions to rewrite?

It would be great if we can see a before and after comparison.

Clojure is a pretty simple language. Ive taught it to junior devs with only Java experience and they learn it within a few weeks without any major problems.

It's a business struggle. You're not going to hire Clojure devs. You're just not.

So every hire will be a cost to cross train through language as well as get used to the codebase. And a lot of more seasoned devs are very binary about lisps. I personally wouldn't work at a place that wanted me to do Clojure over Scala/Java/Kotlin.

I worked at a globally known media company that were primarily a Scala company and they had made a handful of applications from people with Scala experience a year at best. The churn of every skilled dev leaving being replaced by someone very green is a horrible opportunity cost even for a large business.

I'm a sometimes hiring manager and often a team lead on Clojure projects. I have never had an issue hiring someone who wanted to work in Clojure. It's much harder finding devs worth hiring than finding devs who have immunity to the terrible parens allergy.

Even training devs isn't an issue. C#, Java, Python, PHP, C, JavaScript, and Ruby developers have had an easy time being productive on the first day because of how high level the language is. Projects tend to be very domain-driven, and a decent editor config let's people get started right away. Within a week of reading code and a tutorial on syntax, I have a perfectly reasonable dev on my team making meaningful contributions to maintenance and feature development. Locality, immutability, and brevity make for an exceptionally fast on boarding experience even when the dev had never used clojure or a lisp before.

Basically: companies are looking for cheapest programmers they can find. That involves using the stacks most popular on the market.

(I remember my boss at one of my previous jobs literally saying to me "you know, we could let you write that in something more advanced, like Ruby or Python, but then when I'll be hiring, I'll have to pay 2x to a Ruby programmer than I have for a PHP programmer, so PHP it is".)

...And you and I both know they are ignoring the much bigger long-term cost and prefer to go for short-term savings. And I think most programmers know how well does that end, historically.

Shortsightedness is not a new invention.

I have plenty of anecdata for when I invested in a better tech fit without asking anybody and it always ended up great. EVERY. SINGLE. TIME. I got a few slaps on the wrist because managers don't like not controlling how you breathe but even they were forced to admit that I saved them money in the mid- and long-term.

I am getting old and cynical and quite frankly I never ask business people questions I know the answer to anymore. I also don't go fanboy mode about languages and tech anymore. If I find a better alternative for something that keeps bleeding my employer's money and human resource with no end in sight, I'll switch it over on both technical and long-term cost-saving merit.

To hell with anyone who disagrees. My stance these days is -- "I know both programming and some business. You only know business. I am better equipped to make the decision than you."

the problem might be you could make a decision that is advantageous for you but not for the company

Yes. But if you're paying top dollar to hire a trained professional, and then refuse to trust them in their area of expertise, you're wasting money. You should either start hiring cheap staff and micromanage them, or learn to resolve your trust issues and instead focus on things you're presumably a specialist at, which is setting incentives so that hired professionals always have company's best interest in mind.


Related: I sometimes wonder just how much money companies lose on trust issues. Even with regular people, I often see companies spending money doing everything imaginable to make it as difficult as possible for workers - on which, too, they spend money - to do their jobs.

They lose much more than money. Rumors start going around; 99% of the cities in the world aren't THAT big that rumors aren't a thing -- they are. People start distrusting the company, it gets a reputation of "only work there if you're in danger of living on the street", qualified people recommend only their lower-qualified friends (or simply opportunists), etc.

It only goes downhill for these companies and they lack the maturity and intelligence to recognize they brought it upon themselves and maybe start trying to turn things around.

Oh well, screw them.

I stated that my decision has benefited the companies so not sure what you're trying to say.

Fine, it wasn't 100% clear: "it ended up being great every single time". By saying that I didn't mean that I got tingles in my stomach for using some tech, I am saying that I helped an employer stop bleeding money and human resources in a black hole.

It's not only the price, but also availability. In 2010 I had to choose a technology stack for a new project (in Germany). Personally I would have loved to choose Rails. It was very promising and solved many repeating patterns of web dev in a very systematic way. But: I didn't know one single Ruby/Rails dev. Back then not too many startups used Rails in Germany; the most prominent one was Soundcloud. On the other hand I knew many PHP devs, although none of them had programmed in a web framework before, rather they were using their own set of self-developed solutions for various things, Smarty for templating, no ORM, etc. Their approach was way inferior. But nonetheless I decided to choose PHP + the Symfony framework. It was a good decision. Because I had access to PHP devs, I could hire PHP devs, give them enough time to get familiar with the Symfony framework. But most importantly: I could hire them easily in Germany and yes, they did not cost a fortune.

Today I would love to start a project in Elixir/Phoenix (or more realistically: Python/Django), but I don't know any Elixir devs in Berlin. And I don't know too many web devs using Django in Berlin. Again if I had to build a static website, I would choose PHP (+ Laravel framework). And if it was a dynamic web app, I'd choose Node+React. It's all about availability and feasibility.

Edit: punctuation

Several assumptions I feel are wrong:

(1) You keep saying you didn't know technology X or Y devs in Berlin. Sorry to partially derail but why is remote forbidden in your organization? I mean, you're pointing at a limitation that's rather easy to overcome.

(2) Bigger developer availability == good for business, you claim. That's very 50/50, neither side of this argument is anywhere remotely universally correct. "If all you know are hammers all problems look like nails". I've had PHP devs ruin near-perfect projects in teams where I was working. Their reliance on the arcane-but-overall-working comparison operator alone introduced at least 150 bugs over a year.

Bigger dev pool != better for business. It just helps you treat people like replaceable cogs in a bigger machine. That's not the same thing at all.

(3) Known tech != quick onboarding. You yourself pointed out your new hires needed to learn Symfony. And you claim to be interested in Elixir and Phoenix. Well, for one thing, Elixir is extremely easy to learn as a language; secondly, it's OTP (the Erlang / Elixir concurrency framework) that's hard.

So what you said could absolutely apply to Elixir. The language is learned extremely quickly and then people have to spend some time getting to know a framework. It's the same everywhere really.

I feel your comment was putting too much weight on local physical availability of programmers and that's the wrong metric to use when assessing new (and possibly better) tech.

The project had a very tight budget. That's why relocating devs from across Germany / Europe was not an option.

Also, even if the funding was better: One important aspect is: What if key developers (in that fancy new technology) leave the project? How fast can you find new devs? If it is a widespread technology (say: Node/Express + React) there is no risk. But think of hiring Erlang developers in your region.

I like Scheme (and it's influence on JS), I love functional programming, I like Ruby a lot, I really love Elixir. I like Python and Django a lot. But when it comes to setting up a project there are many aspects that determine my decision for technology. Some very important ones: What are my tech alternatives to tackle a problem? How wide spread is that technology in my region? How fast can I hire talented people in that tech?

I do acknowledge that a small team of good Python+Django devs for instance can work more efficiently than a team of PHP devs who don't appreciate software engineering qualities (not saying that there aren't really profound PHP devs out there, but many PHP devs who don't have profound software engineering qualities). There are only three real alternatives for web dev in Germany (if you have a tight budget): PHP based, Node/Express+React/Angular or Ruby/Rails based. I wouldn't even consider Python/Django, and forget about Elixir/Phoenix.

I was not talking about relocating developers to Germany.

I was talking about you bringing in remote Elixir talent. It's not so hard nowadays, the dev base is growing.

Known tech is quicker onboarding. Not all business have the capacity to train devs on new Lang's, but if your company does have that extra capacity then sure, no problem.

You ignored my point but I'll still repeat -- onboarding new language is relatively quick (several days). Onboarding new frameworks for languages people know is much slower.

So no, I disagree. Known language is not quicker onboarding for a framework. You'll save 4-10 workdays out of the several dozens needed for one to become an expert in a framework.

IMO framework + codebase. Even if we're talking about companies using off-the-shelf frameworks and not something homemade, the project using that framework uses it in its own, peculiar and usually undocumented way, and figuring that out is even more difficult than figuring out the framework. This cost is present in pretty much every software job.


It grinds my gears to hear that legend over and over -- "new language is very risky and slow to adopt!", even though a lot of us know that the language itself is the lowest barrier of all.

On top of framework and codebase I'd also add organizational or team coding style. It becomes a bloody mess pretty damn quickly. I've personally spent 6 months being onboarded in a very finely tuned and hacked to oblivion Rails project -- a technology I knew pretty damn well at the the time -- and then it took me 3 weekends to get my own Elixir / Phoenix project off the ground... and for it be useful in business terms as well.

So these "new language risks" are vastly overstated and are simply an excuse for dogmatic people to not act in the project's best interest.

>>That involves using the stacks most popular on the market.

This is one of the most unforgettable lessons I've learned in life-- 90% of the dev hiring market is between 4 languages: Java, Python, Javascript and C.

Beyond this it barely matters how awesome your language is. The manager at the other end is optimizing for per person cost, and trying to hire as many people as he/she can.

We've hired 3 so far. Actually had entirely too many good applications to choose from (25+?).

Leaving this comment so people who may read this and get scared off understand that it's definitely not the case.

> It's a business struggle. You're not going to hire Clojure devs. You're just not.

My company has both a) hired a number of Clojure developers, and b) a number of developers who were good programmers, so switching to Clojure was not a problem.

In general, I noticed that you either hire average programmers (in which case Clojure/ClojureScript could be a problem), or good programmers. Good programmers have no problem with Clojure, regardless whether they know it or not.

Leaving this here because people might think the above comment is universally true. It isn't.

My team has been using Clojure for over 7 years now, We have never had a problem hiring people. You can take somebody who has no Clojure experience, and get them writing useful code literally in a couple of weeks.

Meanwhile, Scala is a much more complex language, and training somebody on it will be a lot more challenging.

I was productive in Scala far faster than I was when I learned Clojure. To be completely fair, I was newer at programming when I learned Clojure, and knowing Clojure definitely helped me learn Scala, so there’s something to be said for Clojure for sure.

But if I had to take an average Java programmer and teach them a new language choosing between Clojure and Scala, I’d choose Scala hands down. The gap in semantics is so much closer, and concepts and libraries are so much more easily translatable. And it is entirely possible to be productive with Scala while knowing <50% of the language.

Scala is a huge language, and many experienced developers have trouble using it effectively. You might learn a subset of Scala that works for you, but sooner or later you'll have to work with code written by others in a style that might be completely foreign. From what I've seen, Scala code ranges anywhere from Java syntax sugar to Haskell fanfic with libraries like scalaz.

Meanwhile, my team regularly hires coop students, and we never had to spend more than a couple of weeks training them to be productive in Clojure. Hiring and training people is simply not a real world problem when it comes to Clojure.

If semantic similarity to Java and translatability to Java libraries are your main criteria surely Kotlin is a much better option than Scala? Kotlin is also much easier to learn than Scala.

Indeed, if you merely want a better java, Kotlin is now the better choice (especially considering the superior tooling). But the limitation is that Kotlin tops out as a better java.

With Scala you can start out in better java territory and go from there to wherever you want. I personally don’t use any of the pure functional libraries, but I still greatly benefit from the strongly and strictly typed but flexible type system. In the 50k+ lines on my most recent project, I can count the number of bugs that escaped the type checker with one hand.

I'm the exact opposite. I liked Scala, but Clojure is much simpler, and I found myself much more productive in Clojure. Anecdotal evidence cuts both ways.

> It's a business struggle. You're not going to hire Clojure devs. You're just not.

How big of a problem this is depends on the team and the company.

I've worked on plenty of medium (30+ dev) projects with <10% yearly turnover and for these teams learning Clojure is practically no cost.

I've also worked on teams with 70%+ yearly turnover and teaching anyone anything was a waste of time.

Media companies tend to be on the higher turnover side and I wouldn't recommend Scala or Clojure.

"70%+ yearly turnover" Much higher than the food store I worked at as a student, long ago. If success is impossible because of high turnover, surely the language will not matter. Ironically, it is possible if there are management problems causing extreme turnover, then Clojure would be a strong advantage for management as a scapegoat.

I was on a major automation project where mgmt nailing down the specifications across four existing departments procedures took over a year... hard to imagine devs working less than a year making sense of it.

If you are looking for 100+ developers who already know it, maybe you'll have trouble. But it's absolutely not an issue hiring 20s of developers quickly. I work at company company whose done just this. Felt it important to put this counter example, even though many others have already.

The hard part about Clojure is the editor tooling, like learning Paredit and how to evaluate code in the buffer. And then training yourself to actually do it.

It's similar to training someone to use Vim in that it's quite a different tooling set and workflow they're probably used to. I think that's the main barrier.

The language itself is pleasantly simple. And tools like Paredit make it one of the most pleasant languages to write. It's just hard for someone to appreciate Clojure until they put in the work to credentialize in its tooling.

I recommend taking a look at parinfer as well https://shaunlebron.github.io/parinfer/

> Who is making these decisions to rewrite?

Anecdotal evidence incoming: For any rewrite it's always the developer. Every time. It might sometimes be the manager's decision to do it, but it's always instigated by a developer.

I can confirm this 100%. I've had to work with some really really bad Clojure software that is unbelievably hard to maintain. This is not a fault of the language, but just more proof that no language is a magic bullet.

I would imagine one reason for this is because the real complexity of a project has nothing todo with the programming. It has to do with evolving the business. Were on our 3rd re-write of our business, but the codebase still has legacy systems from the first two drafts. No language can save you from that.

I love it when people make a mess because they didn't really understand the problem initially, then rewrite it in something else and give all credit to the new technology they happened to be using.

When you rewrite a project it's always going to be a lot better, because you have a much better understanding of the problem being solved having done that once already.

Indeed, on anything serious I write it at least twice. Of course, using Clojure makes this easy since there is usually not a lot of code to rewrite. But after you get into something, you know details about what is needed that you simply cannot adequately predict beforehand in a lot of circumstances.

True, but only with small projects. Once initial creators of rather complex project leave then the mess begins. No language will save you from changing requirements, egos or best practices. Will your rewrite of inherited 200KLOC "is always going to be a lot better"?

My thoughts exactly.

There are many ways to use Clojure, the laziest one of course being the least maintainable. No language can subsitute engineering principles, years of experience, empathy etc.

I wrote a few hints of what I consider maintainable here -> https://blog.vemv.net/maintainable-clojure-beyond-aesthetics...

>Rails project has less functions, and with a far more predictable structure.

This is probably true if your building a very very generic small webapp. In which case, maybe use rails if it's the stack your good at.

However, as the complexity of the system grows, the less will easily fit in mvc. When that happens, pushing all the complexity into mvc will make it harder to understand, not easier.

You can do anything you want with rails. You’re not coupled to mvc. At the end of the day it’s a rack app and it’s ruby. Go wild.

You are coupled to mvc, that's the upside to a framework.

That is not true at all. You have Rails at your disposal but if you want to break free of that and use simple ruby classes and handle request/response on your own -- you are very much free to do that.

Perfect example is running Sinatra inside of Rails: https://jacobbednarz.com/posts/sinatra-in-rails

Again: ultimately you have a Rack (ruby, https://rack.github.io) webserver interface that has a Rails app mounted inside of it.

"Rails" is just some code that is running. You can choose to defeat certain parts of it, mount other stuff on the Rack server, turn rails off entirely and just use Activerecord, etc...

I do it all the time.

Sure, I wasn't defending Rails as a generally better solution.

The quoted statement tends to be true regardless!

> Don’t expect much more growth out of this language in terms of new projects written in clojure.

Why would an average dev not be able to maintain a clojure code base? I'm going to guess what you really mean is a average dev that doesn't know clojure. In which case the argument your really making is that its hard to find "clojure devs". Which I think runs contrary to the numbers which suggest many more people wanting to work in clojure then can find jobs. This piece seems to echo that sentiment: https://juxt.pro/blog/posts/clojure-job-market.html

I think by and large companies probably struggle to find clojure devs in there immediate area. Which is why i see all the clojure jobs either

A) remote B) in big cities

So its possible that companies could get great devs for less if they just allowed for remote.

Nothing backed up here by hard evidence, but worth considering that the language might not really be the only factor here.

Could you elaborate more on that, please? I wonder what are challenges of Clojure in large scale or legacy projects. I already know finding devs with Clojure/FP as core skill is harder than e.g. Java devs. But I have also seen Java projects being rewritten to PHP for similar reason so... just curious the other arguments about rewrite.

I started again to rewrite my project from clojure as can be seen on my Github repository (https://github.com/zubairq/gosharedata/) but the problem was NEVER Clojure. By writing Clojure for 5 years it made me a better Javascript programmer I would say!

If a dev can't work with Clojure, I'd be pretty suspect about their coding ability in general. I have yet to work with somebody who couldn't pick it up in a few weeks or so.

I’ve been doing c, c++, and objC for over 20 years and am just getting started with Clojure. Sure, it’s easy to understand the basics of clojure in a few weeks but it’s going to take me a while to stop thinking imperstively and objected oriented. Mutating everything is programmed in me at this point. I think it’s going to take me months to get decent at Clojure if not a year. I’m sold in the ideas but it’s not easy if you have been doing imperative style programming . I’d bet it’s almost easier for a novice to pick up Clojure

It's different when you're learning on your own and when somebody is teaching you. If you're a company hiring new developers, then you already have people familiar with the language to ramp them up. When we hire new people, we do some pairing, and we do code review on pull requests for features.

I also agree that it's sometimes easier for novices to pick up the language. I've seen students become productive faster than people with a lot of background in imperative style.

Right. I think my experience is actually hurting me in this case. Laziness is blowing my mind a little bit too. I’m writing run! A lot in the repl

If a dev is dumb enough to advocate clojure, I'd be pretty suspect about their coding ability in general. I have yet to work with a decent programmer who liked it.

We've banned this account for trolling. Please don't create accounts to do that with.

Programming language flamewars (one of the hotter circles of hell) are off topic here, as are all flamewars.

I'm trying to evaluate clojure for standard enterprise applications. But I haven't seen anything on how to model entities and relations without a database:

   Entities:Person, Insurance, Car
   Additional constraint: hasPrimaryDriver(C,P) => drives(P,C).
The domain has cyclic relationships, hence I cannot model it as a tree. If everything is immutable, I have to update all entities that reference each other when replacing one?! What's the standard clojure way to implement it?

I don't quite understand your question. Isn't entity relationship modelling an abstraction over the database rather than something separate? In Clojure raw SQL with something like HugSQL is more idiomatic than anything resembling an ORM though you can try to use Clojure as a simpler way of accessing Java classes, in which case all the usual suspects are available.

This would do the trick: https://github.com/tonsky/datascript

This was one of the questions (unarticulated at the time) that kept me away from value-based (immutable) programming for a long time.

The answer is straight forward: you use unique identifiers instead of references. A lot of your code would become similar to how you write SQL queries. You always use `id` to cross-reference entities. You can use whatever enumerable functions are in the language - List.find, Map.find etc.

Now if you want to mutate a leaf node inside a tree, well that question is itself invalid. You can't "mutate" a "value". Can you say mutate the number 12? Or the letter 'B'? Imagine every complex data-structure was atomic like this: the array [1,2,3] always remain [1,2,3]. You can't mutate it because it is one single value; you can get information from inside it, but that doesn't mean you can go and change it. If you do want to change it, then you "transform" it into a new value. So instead of using `array.push` in Javascript, you'd use `array.concat` which provides a new value. Or even better you always use the rest operator `{...oldHash, someKey: newValue}`.

This also means your complex, large tree structure is a "value". You can't mutate it. You can however transform it into a new value. So if your leaf node has to change, then it means what you now have is an entirely new value. The old value still exist; any variables that pointed to the old value still point to that, because, duh, values don't change. Rich Hickey has articulated all this really well in his talk "The Value of Values" (https://github.com/matthiasn/talk-transcripts/blob/master/Hi...)

You'd now be rightly vary of the space-time implications of copying over large sets of data everytime you want it changed a tiny bit. This is solved in functional languages by using structural sharing. When you get a new tree value with just one node changed, the runtime would actually share most of the tree structure, and newly create just the one we want changed. It'll update the reference pointer of the root node, and the entire path that goes from the root till the leaf that changed. This means if you do a shallow comparison between the new tree value and the old tree value, you'll immediately know they're different. You can also safely assume that the entire path till the changed leaf has a new reference and you can do quick shallow comparisons.

This is however an optimization and implementation detail. We're able to do this because structural sharing still allows us to keep the semantic model of everything is value for all practical purposes yet have the benefits of immutability.

OCaml lets you do both though - it supports the imperative place-oriented way of programming and value-oriented programming. It encourages value-oriented programming through its syntax and runtime library, but when you need it (for performance reasons), it is there for you.

What domain were these projects in? What was the 'average' developer unable to grok?

They can't grok how they will get a higher paid job after once they reset the "years' experience" clock on their chosen stack.

There's no worse student than the one that thinks he doesn't have anything to learn anymore...


I've been debating whether or not to give a talk / write a blog post on this, but I think this might just be the kind of thing that people make their assumptions about, and thats it.

I do wish people would give clojure a real go, not just running a toy program, but taking the time to setup a REPL in clojure and clojurescript, learn about sending commands from the editor directly into the REPL, etc.

The reasons for choosing clojure are un-obvious at first glance. Moving from Java -> Ruby for example is something that you feel immediately. People understand in < 5 mins why ruby might be a better fit for a number of solutions then Java.

I don't dig clojure because it's a LISP, or it's homoiconic, or because of most of the academic ideas here in this thread. I picked clojure because with it, I get clojure and clojurescript.

With clojure I get to target all java libraries, great concurrency primitives, the ability to live code a running system, the ability to experiment extremely quickly due to the REPL based workflow, now with spec/generative testing, I get better testing then I had before, with writing fewer tests. I get systems are often easier to reason about or debug. There's rarely a ton of libraries to do a single thing. Existing libraries don't change much. We launched our first service in clojure 1.6, we've never had to do anything but bump the version number. No breakage.

With clojurescript we get just as many benefits. CLJS gives us access to all javascript libraries that have been written, Re-Frame/Om Next are incredible frameworks on top of react, figwheel allows us to never (rarely actually) have to refresh a page when doing layout / css things. Because we have access to a running REPL we can change the state of the app via function, allowing for way rigamarole when trying to test out inputs, etc. devcards lets you look at all the state of your widgets on the page + figwheel means you can see how CSS changes will affect all the states of your widget at once.

Synchronicity between the two languages allows us to write .cljc files where share functions and schemas can exist, so your backend and front end are pointing to the same place for critical things that should be shared.

It's really really different, but it's really really great. If you're a professional in the field, you owe it to yourself to just try it out and see if it helps you save time and be more productive.

All that said, it's not perfect and there are annoying things about it, so I'm not painting it as a perfect language. And there are problems that it's probably not well suited to solve.

I discovered clojure about 25 years into my programming career, and while it introduced new "annoying things", it resolved most of the annoying things I had dealt with over the past 25 years in most other languages.


Discussion from a couple of years ago (97 comments):


> As the project matures bugs are inevitably found or new features are added, these correspond the occasional spikes in the activity. However, if the initial phase of the project can't be completed in a timely fashion then nothing else can happen.

> In my experience, if you can't get something interesting working in a few days the likelihood of actually finishing the project starts rapidly approaching zero.

> With Clojure you can get things done fast, fast enough that you get something working while the initial spark of excitement is still present. I would hazard to guess that this is the reason why Clojure has so many great libraries despite being a young language.

Python dev here who chooses his own tools. I want to move to Clojure, but Clojure and the JVM just aren't that strong in machine learning and ML is my current "growth" area.

Even with JVM based solutions, such as Stanford CoreNLP Python has more support/interest than Clojure.

I am not here to pump up Python and knock Clojure, just to illustrate there are areas that Clojure has a ton more wood to chop. And I do understand there have been significant efforts made in this arena lately. e.g. Neanderthal


Take a look at Cortext https://github.com/thinktopic/cortex

There's a recent presentation that makes a good case for using Clojure over Python for ML https://www.youtube.com/watch?v=0m6wz2vClQI

Having worked with both Lisp and Python, I feel relatively comfortable saying the only thing missing from Python is macros; and when it comes to readable code, it's not a debilitating loss. Much of a macro's capability to create DSLs can be replicated using the introspection and metaprogramming.

There are also some lisps that target the Python VM, so you could consider using one of those.

Yes, Hy (http://hylang.org) is similar to Clojure and very nice to work with. Sadly, they had to abandon their implementation of let recently.

> I am not here to pump up Python and knock Clojure, just to illustrate there are areas that Clojure has a ton more wood to chop

If you are talking about ML, then basically every language has "wood to chop" when compared to python.

probably not R, though.

When i looked at the eco system there seemed like a pretty big gab between R and Python in ML

> Some languages are simple but they're also verbose. You've probably heard people say that verbosity really doesn't matter. These people will go to great length to point out that all languages are Turing complete and that in certain languages you simply have to write a bit more code.

This argument I usually dismiss by suggesting moving to INTERCAL or Brainfuck. Even syntax matters a lot more than people care to admit. The best language is the one with which you can frictionlessly reason about and express a problem without impediment from syntax, paradigms, concepts and boilerplate. Computer languages just as much as natural ones are deeply tied to the way we think[0].

[0]: https://en.wikipedia.org/wiki/Linguistic_relativity#Programm...

I agree, and that's one of many reasons I like Clojure so much. I find its syntax maps very cleanly to the way I think. It's one of the most readable languages I've worked with. It primarily has to do with Clojure syntax being simple and consistent.

Is this the same author as this book or is there plagiarism going on: https://www.amazon.com/Web-Development-Clojure-Build-Bulletp...

Go down to the "From the Publisher" section and you'll see:

> "But the practical question is how well the language maps to the problem to be solved. Does the language let you think in terms of your problem domain, or do you have to keep translating domain concepts in the constructs of the language? The best case is when you can use the language without thinking about it."

Same author.

The sad fact of life is that large corporations often dictate the platform. If you're a mobile app developer, for example, you're only native options are Swift, Java and Kotlin unless you're prepared to settle for a less efficient end product (Xamarin, Scala/Android, Clojure/Android).

There's some real niche to Clojurescript + React Native; one might even outperform a plain React Native app

I work in corporate environment, and it's absolutely possible to use Clojure there. It's going to be harder to get buy in than in a small company or a startup, but not impossible.

Job trends play a role too. I really like Clojure and would like to keep using it. But it's less likely to help get me a job than say learning Python.

I also feel this pain. The reward is high though (salary, remote, colleagues) so I remain motivated.

One also can help creating job market by

- starting up

- introducing Clojure at work

- evangelizing it at local meetups (e.g. hijack a JS meetup, explaining cljs goodness)

I've been thinking about the latter, as a consultant creating a 'need' for your services would be quite an accomplishment.

I have honestly tried many many times to read books/tutorials for getting started in LISP. I know there are many LISP inspired languages and Clojure is one of them. Where LISP throws me off is the excessive use of parenthesis, up-to the point my mind can't keep track of them anymore and IMHO language uses it's comprehension abilities. I am still struggling and pushing hard to learn it; hopefully I can get over it and experience the most powerful language. Any good starting projects/ideas I should try doing in LISP/Clojure etc. to help me out?

Try once more. Pick a small project and just try and try again until you get it written.

Tip #1 - spend more time in the repl than in the editor. The repl lets you figure out and grok the small things.

Tip #2 - compose the small things into bigger things. (And if you find yourself composing too many semi-complex expressions into on big expression, feel free to take any smaller grouping of expressions and turn them into a function.

You may start out very disoriented; but if you keep trying, at some point after trying (perhaps for a few evenings, consistently) it will just suddenly click. It's a wonderful feeling to suddenly get it.

One last tip - Everything is really just this pattern: open paren, operator, some number of parameters/data, close paren. I'm not a Lisp teacher, so I may be stating this inaccurately, but it's just that simple. The pieces may look like more than what I just described, but they are really that simple.

And on the topic of "too many parens", compared to Javascript, C/C++, Java and many other languages, you'll find about the same number of them. They're just arranged differently (and more uniformly in Lisp). Better yet, because they're so uniform, you can completely ignore/forget about the end of big expressions with their )))). Ignore it. Use an editor that matches braces, (I like rainbow color matching).

And now, a warning. If you take the time to really learn Clojure, there's a good chance you'll love it. Then you will find that in most companies, you won't be able to use it unless you sneak it in. Then you'll end up in Elixir because it "looks like" Ruby, and because everyone bought into Rails. But you'll still wish you were using Clojure.

> And on the topic of "too many parens", compared to Javascript, C/C++, Java and many other languages, you'll find about the same number of them.

Exactly. Even more, if you start counting {}, <> and []...

Also, before someone says "but prefix notation!", all mainstream languages are prefix too everywhere except math. That is,

  frob(barify(foo, bar))
is prefix notation, and equivalent Lisp is achieved by just shifting the opening paren to the left of the operator name (and dropping commas, thanks to the uniform syntax):

  (frob (barify foo bar))

Adoption of Elixir is no higher than Clojure. Slightly lower, I'd say. The bigger danger of loving Clojure is going back to working with OOP. Clojure can seriously damage your tolerance for OOP and in today's job market that's a real handicap.

For me, Elixir is a way to do Rails-like web development with much better runtime performance, plus the big bonus of functional programming. And because Rails was such a success (I still get recruiter spam constantly for Rails positions, even after 10 years), I think Phoenix+Elixir will gradually come out on top. It's just a shame that I fell in love with the Lispiness of Clojure. Do...End...sigh.

Beyond the superficial syntax similarities how can developing in a functional language possibly be Rails-like? Mutable OO and immutable FP are opposite ends of the spectrum. I never bought this Elixir for Rubyists sales pitch. Phoenix does't even use an ORM. That said, I really like Phoenix.

I too get a lot of Rails spam from recruiters which I find hard to fathom since Rails usage is on the decline. What's your take on this? Is it that Rails shops will only settle for senior developers?

Syntax similarity probably reduces the "fear of new" (different) that prevents some people from making the transition. But the biggest factor is that the creator of Elixir, José Valim, is a well respected Rails contributor.

But indeed, Elixir is so different from Ruby that one doesn't just casually move to it from Ruby without learning to think functionally.

To your question about Rails, I'm not sure I can say whether it's in decline or not. But I do know that many hiring managers are not technical enough to know what's important, so they try to replace exiting employees with people who have the same skills. And probably there are one or more Rails apps that still need dev/maintenance. As far as money goes, Rails is still a very lucrative career focus, at least in the US.

Rails adoption in the U.S. relative to it competitors - Laravel, Node and Django - is much higher than anywhere else. Outside London Rails adoption is very low in Europe.

It is a handicap but anyone paying attention can see that reactive ui and concurrency are becoming mainstream . Functional / immutable languages seems like the only sane way to tackle these issues. I started writing some Reactive JS code and wanted to barf when I saw what the code looks like .

Hi :)

I feel your pain here and just wanted to let you know how common this is for programmers who initially try out Lisp for the first time. As others have alluded to, I promise this initial "parenthesis shock" goes away rather quickly as you read and write Lisp code. In my direct experience, I have hired junior developers straight out of a coding bootcamp who were able to pick up ClojureScript in their first week using Sublime Text with no editor extensions. In other words: you can definitely do this; it's not magic.

I would strongly encourage you to check out Parinfer [1]. Parinfer makes indentation "significant" so you never have to worry about "closing the parens". Basically, if you can handle Python or CoffeeScript indentation, you can handle Lisp. There are no hotkeys to learn and it is available in most common editors today.

For a first project I would recommend rewriting something you have already written and are familiar with. That way you can focus on just the language and not worry about the problem domain or "how to solve it". Just do a 1-to-1 port of something you've already done and compare how the two solutions are the same or different.

I'm sorry you are being downvoted and being told to "just use emacs" or "use the REPL more". IMO, these are not helpful suggestions for someone new to the language.

Good luck. You won't regret learning Clojure. One of the best things I have ever done for my career.


Last time I checked there were not more parenthesis in lisp than parenthesis+brackets+curly brackets on other languages.

If you need the visual differences between those various kind of parentheses, many lisps and schemes allows to use and mix any of those.

If you are confused because the parentheses are not in the usual place, then I think you are trying to solve problems that are too trivial if such a tiny bit of syntax get all the attention. Or, that you are trying Lisp on problems that are not a good fit. Obviously, lisp (or ML, for that matter) shines at manipulating symbols, not bytes.

I do not personally believe that lisp is, in general, "the most powerful language", but there is a problem where it can be said that it is the most powerful language: it's to write a Lisp interpreter.

Maybe that's the good starting project you are looking for? If so, I can't recommend enough the book "lisp in small pieces".

> Last time I checked there were not more parenthesis in lisp than parenthesis+brackets+curly brackets on other languages.

This is true a lot of the time. Also, commas don't exist in Clojure.

So you might have this in a C-like language:

     if ((foo > bar) && (bar > boo)) {
     } else {
And in Clojure:

     (if (> foo bar boo)
         (doSomething a b c)
         (doSomethingElse d e f))
Another interesting side effect is that in C-like lang, one my say that due to precedence rules, (foo > bar) does not need the parenthesis since && is lower precedence. Yet, if you weren't sure, you'd have to take a short break and go look that up to be certain. Most devs will just keep the parenthesis instead of take that little break, or they will write them in anyway for readability. In Lisp, precedence rules are never an issue because all operators and functions are applied the same way. So the parenthesis might seem cumbersome at first, but they quickly make syntax quite a lot simpler.

Commas are whitespace in Clojure. You can use them if you want to, it's just not idiomatic.

It's also not idiomatic to write "doSomething" instead of do-something, but I thought those mentions detracted from the point.

And in Scala:

    if(foo > bar && bar > boo) doSomething(a, b, c)
    else doSomethingElse(d, e, f)
> Yet, if you weren't sure, you'd have to take a short break and go look that up to be certain.

If you don't know how logic works then why programming? Also, this is not a "complex rule", this is just BASIC LOGIC. As obvious as it can be: read it aloud and you have it.

Also, what would this even mean:

    (> foo bar boo)
THIS is what we need to check out because it's everything but obvious.

> So the parenthesis might seem cumbersome at first, but they quickly make syntax quite a lot simpler.

It won't, it'll just make the code monoton and harder to look at.

> If you don't know how logic works then why programming?

Logic and precedence rules are two different things. I know plenty of Haskell devs who frequently consult not just the precedence but also the associativity (right/left) or operators or functions when reading or writing their code. C++ also has a very long list of precedence rules. It's not trivial and many find the lack of these added complexities an advantage in the simple lisp syntax.

> It won't, it'll just make the code monoton and harder to look at.

True, only if you haven't written much lisp.

I suspect you are trolling here, possibly, so maybe my feedback won't matter.

> Also, what would this even mean:

> (> foo bar boo)

>THIS is what we need to check out because it's everything but obvious.

Any symbol or word in Clojure (or another Lisp) following an open parens is a function call. You literally learn this in the first 2 minutes during the explanation of prefix notation, e.g. (+ 1 2 3) equals 6 or (- 10 1) equals 9. The great thing about Clojure is that > is just a function, not some special notation, so if you are in doubt you can use your editor to click through to the get the function definition (and the actual code). It isn't very hard to train your mind to treat any sequence of (foo x y z) as a function call.

Ok. Function call. Got it.

So what does (> foo bar boo) mean?

foo is greater than bar is greater than boo

or, put another way, the arguments must be in descending order

It's like instead of writing 1 + 2 + 3 + 4 in Clojure, you write:

    (+ 1 2 3 4)
Less characters to type. Because operators are functions, which are always first in a list, normal binary operators like + can take many arguments.

Another benefit of this is function application. If you have a vector/array of numbers and you want to add them all up, just apply them as arguments to +:

    (apply + [1 2 3 4 5 6 ...])
If you had an array some-array that had hundreds of numbers, this would be trivial without doing anything other than using the same basic + function:

   (apply + some-array)
And since you are expressing the arguments as a single piece of data here, and you can have functions that are much more interesting than +, you can see where this leads.... building up arguments as data, using them in function application. It gets very interesting very fast and the language unlocks a lot of ideas you just don't get in other languages.

I suppose my original point was that its not obvious, because (+ 1 2 3) behaves differently than (> 1 2 3). Mapping a binary operator to a list is obvious if the type of the result is different than the type of the operands.

I also find 'Less characters to type' a weak argument at the level of a handful of plus signs, but I grant that once you've made the mental leap there is less cognitive overhead. In the '(> x y z)' if I knew to read '>' as 'is descending' the meaning would have been obvious, but reading it as 'greater than' didn't make sense to me.

If you can agree that (> a b) makes easy sense, that a is greater than b (it's the same symbol as a > b, just in a different place), then I don't see how that's any more difficult than (> a b c), which means that b is also greater than c. But then I've been staring at lisp code for years.

Since `>` is a function, you can just read the function doc (you can do this by calling (doc >) in the repl):

    user=> (doc >)
    ([x] [x y] [x y & more])
      Returns non-nil if nums are in monotonically decreasing order,
      otherwise false.
So (> foo bar boo) essentially checks if bar is between foo and boo.

'nums'? Where did that come from? I assume x and y do not have to be nums?

> and < are for numbers, so yes x and y are nums.

If you instead want to compare or sort other values, Clojure offers other functions for that purpose where you can specify in great detail how things should be compared. If > could operate on strings, for example, what would be the expected result? The shorter the string, the "lesser" the value, or the string that starts with the "lower" letter, etc... it's quite ambiguous. You need more than < or > for that kind of comparison.

It means, literally: call a function named '>' and pass it the three arguments specified. That's it.

You may ask what does the function > do when called, but that's a different question and has nothing to do with a language's syntax and semantics.

Also, it's exactly the same with non-Lisp languages. If you don't know Python and see a fragment of code like this:

    2 ^ 3 ** 3 
wouldn't you have to ask the same question, ie. what do the operators `^` and the other one do when used? However, in this case, you need to also ask about the precedence of the operators, which doesn't exist in Lisp.

Is there no HN rule about making a fresh account just to incessantly troll a single comment section?

You're trolling.

You never "use your mind" to keep track of parentheses in any language - you use the editor's features for this, and read code structure by indentation like any sane human being does (also, it helps that I touch mostly Python ;) ...but for other languages you just configure your editor to reformat you code on every save or on linting). Everyone can get over "parenthesis madness" after configuring their editor properly and a few days of habit. Also, once you learn about "paredit mode" editing, if you like it, parens become a superpower, because you're not "typing code" anymore, you're just using your editor's shortcuts to edit the abstract syntax tree as a tree object that it rightfully is, not an ugly bucket of "code text"...

If you're sincerely not trolling, then Lispers will be glad you don't join in and come and soil their codebases with miss-indented or unnecessarily over-nested because of bad structure code...

The parenthesis disappear from your thoughts after a few days, especially if you are using a proper editor with a feature like paredit.

Clojure improves on this compared to other lisps because it uses brackets and squiggly braces instead of parenthesis for a lot of its syntax, making that aspect much easier to read.

Just stick with it, you will see it is a non-issue after you get started in earnest.

Use an editor that helps balance parentheses and automatically indents Lisp code. If the indentation is consistent with the parentheses (very easy to maintain with a decent editor) then you can mostly ignore the parentheses and just look at the indentation.

Break big functions with deep nesting into smaller functions. (Like in any language). If you have to page up and down several screenfuls to check whether multiple blocks are at the same indentation level and under the same parent, maybe the function is too big.

I wrote a blog post on the issue: https://klibert.pl/posts/tools_for_lisp_syntax.html

I'm linking it because it has a couple of screenshots, which I think works better than just reading a description of how it should look like.

> Where LISP throws me off is the excessive use of parenthesis

There's a standard way to indent lisp (the emacs way, which everybody copies) and as long as it's indented properly you read it like a whitespace significant language (Python, F#, etc).

Clojure is a bit easier to read than other lisps because it uses [] for vectors, arguments, and assignment blocks and {} for maps and sets. Gives the code a bit more texture.

People writing lisp day to day use an editor feature to automatically keep parens balanced (Paredit, Parinfer). Not required since there's a learning curve but it makes editing smoother if you're going to write lisp full time. A text editor that highlights the opening paren when the cursor is on the closing one (pretty common feature) is all you really need.

It also uses fewer parens than other lisps. Clojure let statement (let [foo "bar" baz "qux"] foo) => "bar" Scheme (let ((foo "bar") (baz "qux")) foo) "bar"

So in addition to additional symbols for notating vectors, map, etc, it uses fewer parens in general.

that was literally my story as well until i tried Clojure. i think convenient data literals is the top reason i could stick with it and properly learn a LISP. immutable to the core, nice concurrency primitives, trivial access to the whole java ecosystem - those are just cherries on top.

try using Atom with Parinfer plugin. i vehemently disagree with people trying to push emacs on Clojure/LISP newcomers. having to learn a possibly completely alien editor to be effective programming LISP is another big thing that pushes people away.

If you've done lisp for a while, you don't try to match the parentheses.

Firstly, you have an editor that matches them up for you.

Secondly, you read via indentation and whitespace. And since just about the only thing defining the syntax of lisps is the list structure, the editor also parses and can take care of that relatively automatically too.

I found it took about 6-12 months of exposure, and then your brain just tweaks/snaps/breaks magically one day, they all disappear, and then you no longer see them :P

I read 2 books, watched a training video, and watched a Rich h. Video if I needed inspiration, but it didn’t completely click until I just wrote a small app in it. Just build a script in it to learn. Break your code up into defs and tiny functions to understand what you are trying to do and then put it all together into a function. The only parallel I have professionally is when I try to write very complex sql

Clojure code actually ends up having less parens, and others symbols than most languages. Here's an example comparison with Java https://labs.ig.com/lines-of-code-matter

>The only difference in readability is that the Java version has a lot more parentheses.

Parentheses aren't an issue if you write Lisp in Emacs.

You don't need emacs for good parenthesis control. The same tools emacs uses for this are available in about a dozen editors at least. Vim, Atom, Cursive/IntelliJ, Eclipse, Sublime Text, it's pretty common in most good editors.

Understanding programs with recusion requires actually programming with recusion that many devs just aren't exposed to. It's just not the dominant paradigm

Explicit recursion is rarely used in FP, and it's often a code smell. It's almost always better to use higher order functions instead. The advantage is that you can implement a particular transformation once, check for edge cases, and reuse it from there on. You also end up with declarative code that talks about what it's doing without distracting your with implementation details.

Clojure has a rich set of functions for manipulating data such as map, filter, reduce, and so on. There are also libraries such as Specter https://github.com/nathanmarz/specter that allow you to declaratively transform complex data. I highly recommend watching this https://youtu.be/rh5J4vacG98 short video to see how it works.

You misinterpret my comment thinking it is a statement about myself, when it is a statement about the wider programming community.

My point is that you don't really program with recursion in Clojure, so it's not an actual problem for people using the language.

My point is that there is a learning curve no matter which way you slice it, as one must also learn to use higher-order functions effectively (I do not believe mainstream devs know this innately) and this is an obstacle for newcomers.

Pretty much all mainstream languages use higher order functions nowadays, it really shouldn't be a foreign concept at this point.

The basics of HOF are not foreign concepts at this point (e.g. map, filter). But FP amplifies the degree and nature in which they are used.

for example learning to use folds or transducers. Learning to avoid mutation etc

Why is learning a few FP concepts considered more taxing than the all the cruft which accompanies OOP? My experience is that FP concepts and idioms are a breath of fresh air compared with OOP design patterns.

It is not 'more taxing'. It is less familiar and implies an effort to learn for an existing dev. (I am talking about existing devs, not those learning their first language). The issue for those folks is not absolute difficulty but motivation.

All I can say here is that my team has been using Clojure for over 7 years now, and hiring devs hasn't been a problem. At this point, my view is that if somebody has problems learning these concepts, I probably wouldn't want to hire them to work with any language.

I have no doubt you've had great success with clojure.

However I question generalizing from 'this was not an issue for my team at my company' to 'this should not be a concern for any company under any given constraints'.

I don't doubt most devs wouldn't have problems learning these concepts. It's whether business have capacity to allow those devs to onboard in such a fashion, knowing that different business are subject to different constraints.

There are many companies using Clojure large and small, nobody actually using the language has problems hiring people for it. This is not a theoretical debate, there's no evidence to support the notion that Clojure is hard to hire for.

In practice, the language is a small part of what a new hire has to learn. Each company has different processes, tools, workflows, frameworks, and so on. A language is only a small part of that.

While there are obviously companies where Clojure wouldn't be a good fit, the reasons wouldn't be that it's hard to train new people to work with it.

You don't even have to use Emacs nowadays. Atom has lisp-paredit and parinfer package, VS Code has paredit, Cursive has a structural mode, and so on.

An editor plugin which supports color-coded parens makes the parens problem disappear.

You should try parinfer for your editor, that should help with the parens


(function x y z)

I happen to like Lisp (well, Scheme at least), but I think a better comparison would be:

    let a = 123, b = 456, c = 789;
    # more code

    (let ((a 123)
          (b 456)
          (c 789))
          ; more code
In many cases outside of function calls, there are more parens.

Well, a more accurate comparison would be like this:

    let a = 123, b = 456, c = 789;
    # more code
Because Lisp let explicitly defines its scope, while the first example inherits the scope from whatever it is embedded in.

And in Clojure:

     (let [a 123
           b 456
           c 789]
Clojure syntax was designed with a clear and deliberate attempt to remove many of the parenthesis from lisp syntax. This is just a part of the greater design goal of offering very concise and elegant syntax in general, which is one of the reasons I keep coming back to it.

Speaking as a beginner, I find the pure lisp example easier to read than the one with the clojure literal syntax. I actually don’t like the []. I’m sure I’ll get used to it tho

Yep, Clojure made some nice improvements (still 4 parens, however), but I just can't get excited about the JVM.

The JVM does solve the distribution problem that's heavily frustrated me with other platforms.

I can create a great tool in Ruby for example, but asking a computer illiterate to run it in Windows isn't going to happen.

You can however hand that person a .jar from Clojure and ask them to run it.

Assuming they have Java installed. Which, in my experience, is less and less likely as time goes on, for random samplings of non-developer Windows boxes.

Not anymore! Java now can link JVM, runtime, and your own modules into a bundle that just works and runs everywhere.

On Linux, you’re even able to compile most of it into a single binary.

Good to know. Does it tree-shake to cut down irrelevant stuff out of thefinal package?

That’s exactly why Jigsaw and jlink were so important, because they provide exactly that.

> (still 4 parens, however)

My example has 2 parens.

Even if you're not excited about the JVM, ClojureScript is particularly productive for front end development, where I genuinely love it tremendously.

well, ok but the comparison is moot when you consider that dense lets are rare in Lisp

that should have been object.function(x,y,z) or function(x,y,z) - typo and it's too late to edit

You just have to indent them the same way you'd indent curly braces.

The harder part about lisp is actually recursion. Surprisingly, there aren't a lot of programmers who can follow recursion naturally.

(loop exists in lisp but recursion seems to be the common idiom)

>"The harder part about lisp is actually recursion. Surprisingly, there aren't a lot of programmers who can follow recursion naturally."

All examples of recursion I've ever seen in Lisp follow a similar structure...

* First section checks if recursion is finished. If so, return final result (or final element of full result if returning a list).

* Second section performs an action or two on the inputs.

* Third section calls the function again with revised inputs.

In many ways it's just a do while loop with different syntax.

Not true; loops exist (as a macro) because recursive functions(that naturally correspond to loops and iterations) are harder to read because they hide the logic of termination conditions. Fortunately we are using a powerful language, that lets us transform natural loop syntax to a recursive function.

In my experience you rarely need recursion in Clojure, to the point that it's unidiomatic to use it. For 95% of your looping needs you use combinators, and for the rest there are the loop macros.

I would say nearly all my loops are either map or reduce. That's it.

Downvotes for asking? :/

I'm sorry your original question has been downvoted. I think it's a perfectly reasonable experience report and question. I replied here: https://news.ycombinator.com/item?id=15527041

I have no lisp experience, but I want to ask to who knows more: isn't the use of too many macros a potential hinder to maintainability? What happens to a project when it becomes a mini-language in itself, with its conventions, it's opinions, it's specific flavour? Does this help or discourage the onboarding of new developers?

I know C is not a good example, bit at least in C projects, I was always wary of "smart" developers inventing their own "smart" conventions and syntaxes

Macro use scenarios have to be weighed against the alternative: writing by hand the logic that the machine would have generated from macro invocations.

I liked this quote.

"What matters to me in a language is whether I can use it without thinking about it."

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact