Hacker News new | past | comments | ask | show | jobs | submit login
Why I Switched from Python to Clojure (2016) (bradcypert.com)
211 points by kermatt on March 24, 2019 | hide | past | favorite | 194 comments



It's interesting that the very things that attracted the author to Clojure was what kept me from moving to it from Python.

I enjoyed the syntax. Loved the immutability. However, I wasn't able to understand the structure of program data at a glance even when reading my own code. The reliance on lists and maps everywhere meant that the structure of data was encoded in the code of the functions that created it and sometimes you had to go several functions deep just to understand the bit you wanted.

In Python if I'm returning a tuple that's more than 2 or 3 elements long, I know that it's time for, at the very least, a namedtuple because I've had to deal with code in the past that has mysterious 5-tuples etc that just becomes too tiring to deal with.

To be clear, big collections in Python aren't a problem as long as the type of the contained data remains uniform.


I feel like people who come to Clojure forget the lessons that were known from the time of SICP that just because you can program using data, does not excuse not building up an abstraction tower.

If you have entities in your domain, presenting clean and encapsulated ways of interacting with them is a separate concern from navigating and modifying their underlying datastructure, which is an implementation detail.

IMO the "it's just data" benefits of Clojure are that a lot of the common patterns which might have felt like being macro-worthy in CL/Scheme are achievable using things like keywords, which leads to more uniform code (rather than ad-hoc project-specific macros).


While this approach did strike me as quite elegant in theory, what I found in practice was that a lot of those interfaces ended up just being boilerplate to give me access to the underlying elements of the data which I would have gotten for free in a more strongly typed language.

In other situations, this approach would work well as long as what I was working on was fresh in my mind. However, when I had to come back to some code and extend its behaviour it would be back to digging through it to either understand the structure of the data... or understand the structure of the fancy abstraction I had built up on top of the data. It just didn't seem to be very time efficient in practice for the things I was trying to build.


That's an interesting insight - isn't the whole point of the indirection to free you from needing to understand the underlying data structure when extending things?

I feel the heart of the issue is surrounding a similar problem that message-passing set out to solve, perhaps encapsulation? You set up some boundaries to a thing and define some contracts for interacting with it, and in return you are free to separate the implementation of that from the consumer of that data.

I worry that when people program in Clojure, they forget all the discipline that was embedded in some of the better parts of the previous language/paradigms they came from and mistake that as liberation.


The fundamental problem with S-expressions is that they are intended for (1950s) machine readability and human ergonomics takes a back seat.


Ah, the good old S-expression flamewar again! Every few years it comes up.

This is, of course, mostly a matter of opinion and experience, but I find S-expressions highly readable and LISP syntax superior in readability to every other syntax I've seen, as long as different parentheses are allowed like in all modern LISP dialects.

There are two other problems with LISP. First, it's so powerful that the semantics gets too rich, every author creates a DSL of its own with tons of macros and functions for every data structure. That makes code unreadable and difficult to maintain in the long run. At some point, every LISP program starts to look like a hack and you start spending most of your time transforming one data structure to another to interface with various packages.

The second problem is the focus on dynamic typing. I've found strictly static compilation better for debugging. Racket is particularly bad in that respect, as it tends to use ad hoc symbols with contracts everywhere. Static enumeration types are just way better for that purpose. Ideally, they should even be tied to the functions they are used in (e.g. a "function parameter type" with appropriate scoping).

These are the main drawbacks of LISP, maintainability and readability of code written by others is too low, because of a tendency to encourage hacks and DSLs.


One could say the same for almost all language. One can abuse java reflection and do all kinds of unreadable magic and code generation.

If I can get one thing, I'd probably say practice in immutability. This simplifies a lot of things, even in java. In Clojure, immutability is first-class.


sure, because Sure, because this (taken from React's JSX example page):

    <DashboardUnit data-index="2">
      <h1>Scores</h1>
      <Scoreboard className="results" scores={gameScores} />
    </DashboardUnit>;
    
is more readable than this:

    [dashboard-unit {:data-index 2}
      [:h1 "Scores"]
      [scoreboard {:class-name "results"
                   :scores game-scores]]
                   
Please tell me more about Lisp readability.


I don't really see how JSX informs us about readability of s-expression vis-à-vis Python et al.

Disregarding that, and in my opinion, properly formatted JSX might be a bit worse then s-expr for shorter stretches of code, but if/when you have a longer stretch, the visual symmetry in JSX would seem to tip the scale the other way. If there is a point to JSX, I would claim it's mostly that it looks different than the surrounding code that is the point, as it potentially makes it easier to parse and to clearly separate roles between code and design.

While I'm frankly not a fan of either of the above-mentioned, I do find some of the arguments around the brilliance of s-expr to be perplexing. The most confusing argument is the one that s-expressions are good because everything looks the same, which to me sound like arguing that the best way to paint is to only use one color, which I vehemently disagree with.

I think the easiest explanation of this conundrum boils down to a rather simple fact. Visual regularity is as confusing to some, as the lack of it is to others. People are different, sometimes shockingly so.


> The most confusing argument is the one that s-expressions are good because everything looks the same, which to me sound like arguing that the best way to paint is to only use one color, which I vehemently disagree with.

The point of the syntax isn't necessarily that everything looks the same, it's more around why that's the case and what other properties it enables. In other words, if a homogeneous (and arguably less convenient) syntax is the downside, it's important to consider the upside too. This still not make it 'better' enough for to want to use, but maybe will shed some light on why reasonable people might think differently.

The core concept behind Lisp syntax is that Lisp has a handful of core data structures, and the user syntax of the language is defined directly in terms of those structures. Clojure uses sequences, vectors, hashes to do things like represent blocks of code, argument lists, and type declarations. The structures used for this are the same as the structures uses in user code, and what you type in to the keyboard when you program are the normal textual serializations of these structures.

So this gives a couple of benefits:

1) The character sequences used to represent the language are easy to parse in a structural way without a whole bunch of parsing logic. A structural editor for Lisp doesn't necessarily need as complete a grammar or parser to enable the structural features as does a language like Java, Scala, C, etc.

2) It's easier to generate or manipulate code. Most famously, this enables things like macros. Macros and code transformations are absolutely possible in languages like Java and the JS ecosystem, but less commonly used and more expensive to develop.

There are many other examples of where these properties come in handy, but JSX turns out to be a reasonable illustration of why these are useful. Adding something like JSX to Javascript requires an explicit preprocessing state. Then, it also requires specific editor support to deal with the modified syntax. Once all that's done, what you have is a wrapper around 'React.createElement'.

It says a lot about the value of application-specific syntax that people are willing to pay those costs to get a certain syntax, but the Clojure/Lisp approach (hiccup for markup) reduces the costs and makes that kind of thing significantly easier to do. Whether or not that's the tradeoff you choose to make is one thing, but there are reasons to make it.


For someone familiar with HTML/XML, so any developer in the last 30 years, yes it is. Your point being?

I like the ideas behind clojure, I really do. I even wrote a non-trivial personal project with it. However, coming back to that code after two months was basically like the plot of Memento. I ended up rewriting it in JavaScript instead.


My point being is: people often complain about readability of Clojure and Lisps in general without even giving it a heartfelt attempt. They complain about parentheses (which becomes a non-issue within literally hours after learning structured editing idioms). It's a matter of familiarity.

Some prefer semicolons and other "visual garbage" in their code, I like minimalism and structure.

No other language can retain readability on different screens like Clojure can. Even reading it in a narrow screen of a mobile phone - it would wrap, but still retain its readability. Good luck trying that with literally any other (non-lispy) language.


Almost anything (if not all) requires one to put in years of practice before one is adept. "a" non-trivial "personal" project probably won't cut it.

Comparing that to your 30 years practice of XML is not at all useful.


I didn't say anywhere I have 30 years of experience with XML. What I said is, EVERYONE is familiar with the syntax. JSX is especially suited for expressing HTML because it is, in fact, almost HTML.

As to your other point - maybe. I'm not seeing anywhere near enough potential payoff to invest years into it, though. Also, I'd argue that makes it a terribly impractical language if you're interested in getting work done.

To clarify, I don't agree one needs years to become competent in clojure. But if one did, that just makes it a worse value proposition.


I'd rather work with Clojure than the monstrosity we make with our 30 years of experience.

Sure you can get things done and ignore the maintenance horror later.

Anyways, in the 30 years we at least steadily transfer features from Lisp.


Why rewriting it in js help with maintenance?

This is not a snarky question, I'm genuinely curious since I personally have no problem coming back to old clojure code.


I can read the JavaScript I wrote months or years ago with very little effort. The language is fairly trivial, and the big gotchas have been mostly addressed by es6 and avoiding type coercion. I have limited time to dick around on personal projects and I mostly wrote Python and JS for a living, so having a familiar syntax means the difference between spending my time building cool stuff versus spending my time trying to remember how to read paren soup.

The other factors are cultural - so much of the clojure ecosystem is badly documented, or not at all. Sure, I can go and read your code to figure out what it does. But that comes back to the limited time argument, unless I'm paid for it, I'd rather write stuff in a language with a culture of proper documentation. For a recent example that does an outstanding job, see Elixir.

Finally, the ecosystem just feels... abandoned? Like walking through a ghost town. A lot of things on GitHub look incredible, but if there's no commits in the past 3 years I'm not inclined to invest time in it. I know the common argument about them being finished, and I don't buy it - non-trivial code rarely is.


Basically damnation with faint praise?


I genuinely can't tell if you're being sarcastic.

The JSX looks immediately intelligible to me because I'm used to looking at HTML. The Clojure looks unfamiliar but it's not so strange that I can't imagine it feeling just as natural a way to represent the same data structure. It would just take a little while to get used to.

The major issue I suppose is that this example is supposed to render to XML, which is closer to JSX than Clojure. But by that standard Vue (or XSLT) is even closer.

I've done a little professional work in Clojure and wasn't a huge fan, but it didn't seem particularly bad (syntactically). It just seemed like something you had to get used to.


> The Clojure looks unfamiliar

That's my point. It is simply "unfamiliar" to majority of developers who have never tried Lisps. And a lot of people throw baseless claims it to be "less readable" without even given it a try.

A few years ago plain English to me was "unreadable". But I have learned the language. And look, today I can even make comments on HN. I guess it's a good thing I haven't dismissed the language because I was unfamiliar with it.


Just about every innovative idea from Lisp, and there were quite a few of them, has been incorporated into modern programming languages. Except for sexpr syntax. Draw your own conclusions.


> Draw your own conclusions

My conclusion is that those languages which incorporate S-exps become identified as some kind of Lisp. This is the case even when it's a misidentification; i.e. little else is there of any Lisp "DNA" other than the parentheses.

Or else, those languages become identified as something that is not modern.

Thus, no modern, non-Lisp language has S-exps.


sexpr syntax has incorporated into 'modern' programming languages like Clojure, LFE, Hy, ...


Indeed. Sexps are a syntax, so incorporating them in a language means you immediately start to look like a Lisp.


Clojure's answer to that problem and a couple of others is clojure.spec


One way to read functional code is to focus on “what” rather than “why” of things similar to Unix commands.


My impression of Python is that it's a Fisher Price Little Tikes coupe with a few GE90 jet turbine hardpoints mounted on it.

It's a very simple language. Literally when things get made to be complex Guido says do it the simple way, because when unbreakable things break it's just that much harder to fix. This makes it very easy to debug, very easy to fix/patch, very easy to onboard new/junior developers in, and very easy to start using in production. It's literally a baby.

But man, you can do some crazy things in Python if you want. You can write C/C++ extensions sure. You can also use things like orchestration with `py4j` and `jpype` in order to execute IPC with a JVM dedicated to the Python process. In that sense all Python in your application becomes a request layer with the JVM or a binary acting as the data layer, and request layers don't require as much in performance because you only need to issue so many requests. Request layers may also need to change faster than data layers/pipelines, and in that Python's great because it's so flexible. Based on some performance testing our team did there's absolutely no reason to simply switch away from Python, though that may also be due to legacy reasons.

I'd like to stick to Python and maintain my Python proficiency going forward (though with Guido out as BFDL we'll have to see how the language evolves). I'd like to maybe add Rust or Elixir to my repertoire and see how they extend my capabilities in ways Python/C/C++ may not. But Python will always be my Swiss Army knife turned combat shovel. It gets the job done.


Are you implying Clojure is not simple? Have you ever watched Rich Hickey's "Simple made easy" talk? If you haven't, I highly recommend it. Disclaimer: It's not about Clojure.


I haven’t watched it, but I have heard about it. In contrast to the simplicity of a programming language in the happy path, I’m talking more about simplicity in the case where things break. For example, Python doesn’t implement tail call optimization in part to eliminate stack trace collapses. On the other hand, many reasons my current tool stops working is due to the underlying configuration of the JVM. Configuration management is a leading problem in distributed systems, and a fairly significant one given how it took down both Google and Facebook in two days. Clojure is a lisp on the JVM; I doubt it matters how pretty you write your code if you don’t say allocate enough memory on the heap for the process, and if it implements tail call recursion when that happens you may get an incomplete stack trace.

This is more speculation, but I do hope it gives some insights into why you may choose one paradigm for a problem over another. Overall I’d say break your problem domain into subdomains and choose the best paradigm for each.


> I doubt it matters how pretty you write your code

Clojure it's not (just about) beautiful and concise code. There's a lot more to it. And JVM in my experience is not a problem. I was skeptical at first, but as it turned out - JVM is pretty stable and solid piece of tech. Besides - there's also Clojurescript (and less popular Clojure CLR). Depending of what you are trying to solve, you can go that route too. I may not convince you to give Clojure a try, but please do yourself a favor and watch/read Rich Hickey's talks. https://changelog.com/posts/rich-hickeys-greatest-hits They are perfectly applicable and do make sense even outside of Clojure context. Many developers characterized those talks to be "eye opening".


I can, and do, appreciate opinions on languages themselves. But my decision to continue using Python over Clojure is much less about the language's syntax and more about the available libraries. How do Clojure web frameworks compare to Django/Flask? What would I use instead of Pandas for data analysis? Are there keras-like deep learning tools?

Switching languages based on a comparison of idiosyncrasies between the two seems like moving to a new city based solely on the weather: a valid consideration, but nowhere near sufficient.


Clojure generally has pretty good library support: the JVM has several high-quality web application servers: Jetty, Netty, Tomcat, Glassfish, etc. And, being a Lisp, Clojure can wrap these with a nice interface. The same is true in other areas: you have very nice access to the entire JVM ecosystem and really-well designed interop with the host language's concepts so, to a large degree, you never run into a situation where you can't do something because there isn't a good library for it: at work, we regularly use Kafka, Avro, Elasticsearch and several other major data systems and we mostly just use thin wrappers around the official Java implementations of the clients for these systems.

Similarly, when connecting to a database like Oracle, it's trivial to just use JDBC/etc. while, for example, my coworkers who are using Haskell generally have to write a Java gateway because there aren't well-tested clients for Haskell.


Curious if your coworkers have looked Eta, a Haskell on the JVM?

Also curious how your Haskellers and Clojurers are peaceably coexisting? (I ask this lightly but still curious :)


I was unaware of this and have been playing with Elm and Haskell in my spare time! Neat to know that there's a Haskell like language on the JVM! Will have to check it out :)


I've talked to them a little bit about Eta and it sounds like there are some concerns about the stability/maturity of the JVM FFI interface, among other things.


I agree. Within the data science community I often see the question of "R versus Python" and it always makes me think of the Old El Paso commercial: ¿por qué no los dos?

I know choosing the right tools for the task can have serious long-term implications on the project, team, or organization level, but individually I get the impression that people worry too much about which language is best when, in reality, many of them are perfectly suitable and appropriate.


For us, the answer to "R vs Python" is "Spark" since eventually you get to dara sizes where just R and Python don't really work.


> where just R and Python don't really work.

From the Spark website: "Write applications quickly in Java, Scala, Python, R, and SQL".


Thats not really how it works. You can write spark code in all those languages, but it's still spark code, running on spark execution engine. When you do stuff, it's on spark dataframes, not on R or Python dataframes.

There's limited interaction with pandas but that's about it - you don't get R statistical algoritms for example.


(I just realised that my previous comment may have come out as rude, I'm sorry for that. I was typing in a hurry and it came out a bit dry).

Yes I agree with your points, what I was trying to convey in my comment is that, since Spark is not a programming language, I think that comparing it to Python or R is a bit of an apples to oranges comparison. The other point that I was trying to make is that both of those languages could be described as first-class citizens within the Spark ecosystem.


Clojure works on the JVM so you have native access to all of the Java ecosystem. With that in mind I'd say the libraries are actually better for Clojure with the exception of data analysis. I don't think there's a language out there that has as many data libraries as Python (like Pandas).


I really don't understand this point about just using Java libraries in Clojure. Why not just use Java? Wouldn't the experience of writing Clojure that fits the Java library's model be horrible?


Not really. Clojure has nice native syntax for Java interop (or JavaScript for ClojureScript). In fact, I'll occasionally use Clojure to explore a Java API while working in Java.

What happens in my experience is that Clojure programs do tend to encapsulate more extensive use of Java interop into their own modules. For more standard or commonly used packages, this means a canned library or similar. (But this notion of abstracting out APIs is just good software engineering in general, and not really specific to Clojure or anything else.)


The main points being:

1. If you need it, and there's not a Clojure alternative, you don't have to write your own. 2. If you have any proprietary .jars or anything like that, you can leverage those services, models, etc when building an existing Clojure app. This is mainly a transition-type thing, in my experience. "Well, we could try Clojure but then we cant reuse any of our existing models." Not entirely true. However, you probably will want to change those from mutable classes pretty quickly :)


Yes, you are suddenly writing java with parentheses.

Don't get me wrong, Clojure is a great language in itself, but the lack of libs make you spent 90% of your time writing interop, you start to ask yourself if it's worth the trouble.


I actually find Clojure's `doto` notation to be the cleanest way to interact pure Java. It's like an improved syntax for Java that's native in Clojure.


Aren't there a lot of data libraries for scala?


Yeah, at the very least, there's Spark, which is reasonably nice to use from Clojure via the Java API:

https://github.com/finalfantasia/apache-spark-examples-in-cl...


Yes but more data engineering than data science (someone else mentioned Spark).


When comparing programming languages / tech stacks, I am also more interested in the full picture (libraries, complexity, performance) than just differences in syntactic sugar. I use https://github.com/gengstrand/clojure-news-feed as a test bed for such evaluations. There, you will find almost feature identical microservices in clojure using ring with the jetty adaptor and python 3 on flask.

The clojure service was in 13 files with a total of 710 Lines of Code. The python service was in 33 files with a total of 1561 LoC.

I also have published load test results of these microservices at http://glennengstrand.info/software/performance/eks/gke where you will find the per minute average throughput for these two microservices to be about the same but the clojure version was about twice as slow as the python version.


I wouldn't say your Python example is comparable to the Clojure code. I'd describe it as "writing java in .py files", actually. No one is forcing you to write classes for everything in Python, having one class per file, and insisting to deserialize everything into classes. Simple functions and native data structures work just as well as in Clojure, and you'd end up with similar code as a result.


rails/Django -> luminous

Flask -> pedestal/clojupture

Relay -> fulcro

Pandas -> clojure.core, specter

Keras -> mxnext/neanderthal


I don't think luminous really compares to rails/django. Luminus is more like a great starting point, with some boilerplate and glue between libraries, while rails/django is way more batteries included.


The only one I miss in Python that is not in any other package is matplotlib. Is there any clojure alternatives?


I've only very briefly used it and am by no means an expert, but perhaps Incanter?[1] Specifically the Plotting section on the Data Sorcery page[2] has more information.

[1] http://incanter.org/

[2] https://data-sorcery.org/contents/


Incanter is a fantastic toolkit. A great suggestion!


there is a "work in progress" thing named "REBL" REBL is "Read Eval Browse Loop". It's a kind of interactive view of any data.

Basead on it's shape, you can extend and create many views.


Blog Post Author Here:

Personal Opinion: Django is a nightmare to work with. It's too opinionated and a pain in the ass. However, I love Flask. I wouldn't be where I am today had I not found Flask in 2012/2013.

If you're looking for web technologies in Clojure, you'll find quite a few good ones. However, I say web technologies instead of frameworks because generally, the Clojure community prefers to compose libraries to suit their needs and not leverage an opinionated framework. However, if you'd like someone to do that composing for you, the Luminus web template is fantastic.

http://www.luminusweb.net/


I'd argue that the JVM is a good move from python. There are a number of rich frame works on the JVM with great tooling. Also if you use PyCharm switching IDEs is easy with Intellij. I switched from python -> F# -> Kotlin.

I do agree the language is part of the battle. The other is tooling, and libraries. F#/dotnet was painful as a linux dev. The JVM works on almost any platform with great tooling.

For Libraries. I haven't found anything akin to django admin, but I don't really miss it. For me

SQLAlchemy -> Jooq

Flask -> Spark/Vertx


I like writing Kotlin, but I feel like I'm still struggling with the "functional" side it. I think I just like writing java too much.


> F#/dotnet was painful as a linux dev.

Which part made made it painful the most?


was F# painful pre-dotnet core? Have you tried it since? I ran into issues with F# and Mono a few years back and haven't tried since core came out.


I will admit that there aren't as many direct Clojure libraries, but many of the core things out there; core.matrix works pretty well for elaborate matrix processing, there are several decent web frameworks, as well as parsing libraries.

There's a metric ton of libraries for Java and Scala out there, and as a consequence of Clojure's ability to use Java stuff, you have access to all of it. I admit I've not done a ton of machine-learning stuff, but I've used Spark ML, which works well.


The data science story in Clojure is good and improving fast. Neanderthal is the fastest matrix library available on the JVM. Bayadera is ridiculously fast probabilistic programming (not to mention Anglican, which has a slightly easier API in exchange for perf). There's a lot of activity around Clojure bindings for MXNet, if you want deep learning. It's not mainstream Python, but it's far from limited by community or libraries.


Clojure lets you leverage the entire Java library ecosystem, which is flippin' huge -- bigger, even, than Python's.


Except for numerics, which is my whole world, where Java is horrific roadkill. FWIIW back 8 years ago I was ---><--- this close to trying to build out an important system with wrapped Fortran in Clojure. People (who even then looked like they didn't know what they were talking about) assured me the JVM would eventually surpass lapack, because MUH JVM, and that has failed to come to pass.


The SciPy stack got me into Python and it making it hard to leave despite becoming 100% fed up with Python's GIL. I need to port some signal processing code I prototyped in Python to Java for a customer, and a survey of libraries for Java is not making me look forward to this effort.


I'd say Clojure has just the right amount of syntactic friction to calling into java, that encourages people to write Clojurey libraries (or wrappers for Java libs) and keep the Javaisms at bay from your code.


Regarding what is available in the Java/JVM ecosystem, here are some Maven Central stats[0]:

  Total number of artifacts indexed (GAV): 3,566,566
  Total number of unique artifacts indexed (GA): 272,507
0 - https://search.maven.org/stats


Sometimes I read these pieces and I wonder how much of the joy of the new language is just having become a better programmer in the meantime.


At least for me, it definitely factors in. Getting a new perspective on problems by using a language with a different toolbox (functional rather than OOP for example) can help with future problems. And it's just good fun to learn a new language just to challenge yourself a bit.


Blog Post Author Here:

You're definitely right. I had a run in with PolyML in College and I was absolutely terrified of any functional language (nearly failed the class, in fact). Before deciding to switch from Python to Clojure, I had used Clojure briefly a few years before and hated it (was writing mostly Groovy at the time).

After some time to grow, I had started caring about different principles in the languages that I used. I had been ruined by mutable state too many times; I got tired of not knowing how to get the number of wheels on your instance of the car class, I got tired of so many things. Clojure was/is a wonderful reprieve from all of these things, but truth be told, I do still struggle with it from time to time. Or at least, it feels like I do.


I have considered multiple times switching from Python to other alternatives. However, I instantly discard any language with a Lisp-like syntax. I just cannot work with it. Being a Haskell hobbyist, I would like Python to be statically type and have better support for pure functional programming, but Python has the libraries that I need.


Every person that I've talked to that seemed to have a strong negative opinion about parens never used it. They just think they won't like it but wouldn't take the minimal amount of time to give it a try with something like paredit. I was also in this camp for a long time until I figured I would give it a serious try instead of disparaging something I had not used. You might consider that creating and editing code with balanced parenthesis doesn't actually require you to insert them manually when you have an editor that will always keep the code data-structure balanced and makes structural editing simple.


I used to hear the exact thing about Python, incidentally. People would complain about how horrible significant whitespace is and how it causes all these problems, yet everyone I spoke to that used Python said the same thing: that it fades away into the background very quickly and ceases to be an issue. This was also my exact experience with significant whitespace.

Lisp parentheses are the exact same way, in my experience. You hate them until you spend a few weeks writing a Lisp and then they just fade into the background, never to bother you again. Then once you learn paredit, it becomes painful to use other languages again! (And now with parinfer, I think about them even less still) Incidentally, in my experience, Clojure typically has the same amount of parentheses as typical OO languages, in some cases fewer, its just that the opening paren is moved one word to the left.

Also, to anybody new to Lisp: try an editor with rainbow parentheses.


For me the ability to edit code structurally (rather than in terms of plain text) in a uniform way is a great advantage of Lisp syntax.


I've been using Scheme as one of my languages for a decade. (Haskell, Python, Ruby, Bash are the main others.) I generally use a preprocessor for each language. The Haskell preprocessor treats indented as code, flush as comments, also supported by my syntax highlighting. Comment characters feel to me like a thousand Hitchcock birds shat on my car. They're so easy to avoid. It also introduces here docs; no language should be so full of itself that it can't embed multiple lines of another language verbatim.

I've gone through several period of straight Lisp parens, and several periods of using a preprocessor. I believe in committing to muscle memory before deciding; I've oscillated back and forth between Querty and Dvorak, for example. I'm more committed to psychological experiments than anyone who claims the parentheses haters are newbies who haven't tried.

Study counting in higher mammals: One finds that "one, two, three, many" is core to all of us, and higher systems are strapped on like the multiple layers of vision mechanisms. Humans can't deal with the tiger tails at the end of lisp expressions. But machines can? Yes, but machines can also handle inferred parentheses. The complainers about parentheses complainers want off-the-shelf support. Real programmers write their own support.

Lisp looks best with indentation to help infer parentheses. Use a pipe "|" to open a parenthesis that auto-closes at the next ")" or the end of the line. Use a dollar "$" (borrowed from Haskell) to open a parenthesis that auto-closes when the indentation returns. Lisp written this way is stunningly beautiful poetry, cleaner than any of the dozens of languages I've programmed in. The remaining parentheses actually matter, so one pays attention to them.

I've spent years each way, back and forth. Inferring parentheses is better, and one can write any tool to follow this syntax.


You would be interested in my old project using `$` as a way for reducing parentheses: http://text.cirru.org/ .


Would you mind sharing some example code on Gist, please ?


> I just cannot work with it.

Can't or won't?

I'm not being facetious, here. I just can't imagine any reason one _couldn't_ work with it, once the syntax is learnt. There's nothing about lisp-like syntax that's inherently hard to learn.

Why is lisp special?


Not only is it not inherently hard to learn, it's arguably one of the easiest syntaxes to learn. ( fn-name param1 param2 ... ) is the syntax.

Once the brain grasps this, a whole layer of complexity just vanishes. Furthermore, not having to use punctuation between items is just heavenly. There's so much less noise compared to Python, and Python isn't even a particularly noisy language.


Only if you don't consider a forest of parens to be noise. I do. I'm sure I could get used to it after a while, but at first glance, Python code is definitely cleaner than Lisp.


"Forest of parens" can be intimidating for sure, but if it's really a forest, then it usually means there's too much nesting. Which is a problem for any language/syntax. Overnested Python code is hard to grasp, and wide fields of whitespaces doesn't make it better than deep forests of parens/curlies, or mounting slabs of begin/end pairs.


Maybe. As someone with a Lisp background moving to Python (day job), all the semantics hidden behind whitespace makes me just a bit nervous. The parens, for me, make structural editing easier and also give a means of keeping the syntax around even when the whitespace gets damaged by whatever editing operations are happening.

(But whitespace sensitivity has always struck me more as a way to enforce specific whitespace conventions than anything else, and even as far back as Pascal I've been religious about maintaining indention in code. It's easy to do with a decent editor (or not) and helps the readability immeasurably.)


It's not a "forest of parens" any more than any language with C style syntax is, they're just in different places (the correct places) and there's no need to distinguish with braces (which you shouldn't need to).


That’s not true. When you’re passing functions to functions inside functions that return functions... it’s a nested mess.

I studied Lisp and Scheme at university and that’s where it had to remain for me: academically entertaining mind puzzles in data structures and algorithms. It’s just not productive or easily graspable from one coder to another.


Tasteful macros can alleviate this, and Clojure's std lib has a few which are just delightful.

The following does exactly what you'd expect, and there are even cooler ones like `some->` or `as->`.

    (->> some-nums
         (map square-root)
         (filter even?)
         set
         count)
This is what lisp buys you, dead simple rules for syntax, with the option to add syntax with macros. If you made a mistake in syntax design, deprecate the lib and make a new macro, it need not be a feature of core language for all eternity.


And then you use a threading macro to assemble a middleware stack, only to find out that the middleware is applied in reverse order of what's listed as an inbound request comes in... (The outermost middleware being listed last in a threading form).

There's a reason for it, for sure, but it's surely a part of the learning curve.


Yep, it's always possible to have a mismatch in assumptions. That one definitely bit me as well. What's important is that the learning curve is not due to arbitrary rules, it's a faithful application of the logic of the macro.

Remember that what is being passed through the threading macro is not the request, but the handler function itself. Each middleware takes the old handler, wraps itself over it to do things before and after the old handler. The threading macro is not a representation of the path your request will take, it is a way to build up a chained function that represents your final handler, which will then receive the request. Suddenly the ordering makes perfect sense :P


Only if you insist on inlining all of the functions. Do yourself a favor and name a few of them. People that come after you will appreciate it, too.


Yes, Python looks cleaner, Lisp thinks cleaner.


Editor tooling matters. Formatting, traversing, matching, and highlighting parenthesis made all the difference for me.


I've programmed for 10 years in Clojure and have many regrets, but I've grown to just accept that Lisp syntax is correct. On day one you need to understand one thing. Years later, you need to understand one thing. And sure, in Clojure they happen to use three types of brackets for brevity but even that's optional. But other languages have dozens if not hundreds of little syntactical edge cases. I personally find that inconsistent mess repulsive.


What are those regrets? ;)


Oh, so many things.

The parallelism isn't actually that powerful, and in 2019 there's very little to set it apart. The building blocks are great, e.g. immutability everywhere, but few of the built-in primitives are actually usable (hidden unconfigurable thread pools etc). Stack traces still regularly horrible. Libraries are regularly abandoned. ClojureScript integrating with npm etc is more stress than you'd like. Core development is haphazard and proudly so. Types are nice, and spec is still up in the air, and slow, and not widely implemented, and will probably be rewritten several more times before being abandoned. Hiring is harder than a dozen other languages.

All that said, there is still amazing stuff happening. I hold out some hope that Dragan's work might spark a bigger stats/ML community on top of Clojure, for example:

https://dragan.rocks/

Every day I can still write code and think "yay, that's cute", and be thankful for the dynamism and clarity it can enable. But I wouldn't recommend it to anybody, for anything really, and I feel sad realising that.


I had lots of pain dealing with CLJS but, since switching to http://shadow-cljs.org as my compiler, I've generally found that things "just work": instead of complicated messing with externs, you just npm install the package and then add `["vega" :as vega]` to your namespace's list of requires.

As far as the stack traces go, I have a hard time understanding why tooling like Cider's stacktrace filtering isn't wider spread: in Cider, when a stack trace pops up, it's one or two clicks to hide most of the things I don't care about and, yet, that information is still available for people working on the compiler or with Java interop.


Thanks for this, shadow-cljs looks very promising.

In terms of stack traces, it's not just the location in your code that you care about, it's the nature of the error. In lieu of spec being ubiquitous or anybody really checking their arguments ever, you're still left looking at 20 lines of stack beyond your own code to realise you've passed the incorrect type or structure somewhere. And even with spec you need additional libraries to actually yield human error messages in a lot of cases.


"I instantly discard any language with a Lisp-like syntax. I just cannot work with it."

I feel the opposite. I avoid any language without a Lisp-like syntax, since its absence is a serious handicap which unnecessarily complicates code and makes it less readable.

I also much prefer the Lisp idiom of using meaningful function and variable names, vs the one-letter names and crypitic operators that are so common in Haskell, OCaml, and SML.


I agree; once I beat the initial scariness of Lisp, I found it hard to go to any non-Lisp language.

Macros, while easy to abuse, are kind of a game-changer; the ability to add new semantics to a language make it borderline impossible for me to go to any language that doesn't have a good macro system.

I started with Clojure, which I still really enjoy, but due to how much I grew to love the `define-syntax` system in Chicken Scheme, that's become my latest weapon of choice.


Bear in mind that often the reason Haskell code uses one-letter names (and this is possibly true for the other MLs) is that your types can be so generic that you can write useful functions without knowing what you're working on. In such situations, `x` is just as good as `thing`, as far as names are concerned.

As for the operators: they are a visual language. We do not speak our punctuation aloud in English, and we use small marks to guide the reader's eye. Often, Haskell operators as similar, and there is a definite pattern.

f $ x means "f applied to x"

f <$> x means "map f over x"

In general, the <> around an operator lifts it to work over a structure. Similarly:

x & f means "f applied to x"

x <&> f means "map f over x"

For the applicative operators like <star>, star> and <star (where "star" should be an asterisk but I can never work out the formatting on this forum), the operators "point" at the values being retained. And so on.


It's cool, and I wouldn't be sad to work with Haskell by any means, but I still think there's an elegance to lisp syntax that can't be beat. There are very few language level rules, everything is composable building blocks.


I far prefer Haskell to Clojure myself but I will say: once you get half-decent at paredit, no syntax even comes close to Clojure’s.

Indeed, if you really want fun, try Hackett: a Haskell variant implemented in and for Scheme.


Just think of the parenthesis as loving warm homoiconic hugs.


Hehe, when teaching my 8yr-old programming (in Python) I call (function call) parenthesis a hug to explain that a function "hugs" all arguments to it. She seems to get that :).


This is a fantastic analogy, thank you for sharing! I found myself really struggling to communicate how a function's arguments got passed to it to my daughter and I'm going to try this the next time I need to explain it.


I'm teaching my 12-yr old daughter Python right now, and this looks like a neat analogy -- I'll try it in a few minutes.


I haven't got much experience with Lisp, but I can imagine that, similarly to Python's significant indentation, it's a shallow weirdness that can be overcome easily after a little while. In both cases a little help from the editor will probably be needed.


You will shoot yourself in the foot less often with Lisp parens than you might with Python whitespace. Most modern editors handle the closing parens anyway.

Plus, many editors without any plugins can jump between matching braces; so moving around the code is very quick compared to Python (unless you have a Python plugin that has block movement shortcuts).


> You will shoot yourself in the foot less often with Lisp parens than you might with Python whitespace.

I'm not letting that stand ;-)

In my experience Python's whitespace generally removes sources of scope/block errors compared to parenthesis and curly-based languages

> unless you have a Python plugin that has block movement shortcuts

Not sure what this means. The only support I need in an editor is the ability to use tab and shift tab on blocks of text to change the indentation level. It's visually obvious when code is correctly indented.


> "It's visually obvious when code is correctly indented. "

Not when the beginning of the code block isn't on screen.

And more to the earlier point, configuring your editor to jump to the beginning from the end or the end from the beginning of a particular block of code going to be trickier than the equivalent in even vim for lisp, which is simply %

(And vim isn't even a particularly good editor for lisp, but out of the box unconfigured, it's better at lisp than it is at python.. % isn't even lisp specific functionality, lisp just happens to use balanced parenthesis which is something nearly every serious text editor happens to handle in a reasonably robust way, because balanced parens are generally useful in many languages, including python!)


> Not when the beginning of the code block isn't on screen.

The length of your code blocks terrify me. :)


It doesn't take more than two dozen or so lines depending on the window arrangement. Nothing truly excessive. Alternatively, viewing the entire code block might mean scrolling up.

If you want scary, check out GNU's implementation of `cat`. I think they hit ten nested indentation levels. Though, because that's C, you can at least jump between the beginning and end of each level by having your editor jump to the matching {}.


> Not sure what this means. The only support I need in an editor is the ability to use tab and shift tab on blocks of text to change the indentation level. It's visually obvious when code is correctly indented.

In Emacs you can use C-M-f and C-M-b to move back and forth over / select paren/bracket/brace delimited blocks, or in vi you can use %, both with 0 configuration in a language-unaware state. If you're editing C or Lisp or whatever, these generic commands are useful. Editors don't tend to have move-to-next-less-indented-line (and similar) commands outside of Python modes or similar, in my experience, and even if they did, they'd need a bit more work than that to work properly because of indenting subexpressions.


> It's visually obvious when code is correctly indented

Not always, in my experience. I’ve lost many hours to debugging only to realise some piece of code was misindented. It doesn’t happen often and usually when code is being moved around, but when it does, its been painful.


The worst I have seen is: There is a merge commit (fixing conflicts) and then, a "if" condition get accidentally de-indented (compared to what it should be), and now this bug is completely invisible and the code will run just fine... (until you figure out what's going on)


Having seen many instances of someone not pasting the full function on a move, the "whitespace defines otherwise invisible changes of scope" is not something that endears me to the language.

I almost buy that it encourages simpler blocks, since complicated ones are impossible to parse out rather quickly. But my later reviews shows it doesn't remove them.


You could try parinfer, it effectively turns lisp (clojure) into a whitespace significant language like python.

You still get the great “execute this block of code in the repl” with the parens too.


Hey look it's the coast guy :)


Python can be statically typed with mypy and type annotations.


Yeah, there are people who simply can't read Lisp syntax. Where I see structure and beauty, they see "murder of parens" (or whatever demeaning collective noun they use to describe it). I call them dyslisplexics.


Blog Post Author Here:

It's really not that bad! Here are some insights into the parens!

1. I've converted Java code over to Clojure and found that it has less parens per file.

2. They're still there, they're just in a different place. `System.out.println("")` vs `(println "")`

3. There are some great tools to help with the Parens. If you're using Intellij, Pareninfer and Rainbow parens are two extremely helpful tools.


I have considered Python many times, but I cannot work with a language that considers whitespaces as required syntax. I just cannot work with it.


Whitespace is not any more significant in Python than most languages.

Indentation is significant.

Just replace '(' with space/tab and ')' with "deindent". Done.


Have you done indentation with anything else other than whitespace?

I prefer to let this idea of required indentation to FORTRAN.


the downvotes are funny because this was exactly the criticism Python got before it became popular. Probably still gets it.


That's a shame. I use Haskell but wish it had paren syntax. I'd rather have strong types than sexps so I stick with Haskell.


Lately I've been playing with Coconut [1], a functional language that is a strict superset of Python 3. It transpires to Python and can use all of Python's libraries. I've been playing with it lately and quite like it.

[1] http://coconut-lang.org


Try, it's wonderful. The best. I write C and Python for a living.


I notice this was written in 2016, but I wonder if the author's opinion has changed since dataclasses have been implemented in Python.


Python's async support also seems to be getting better. There's a flask clone called Quart (http://pgjones.gitlab.io/quart/) and an async postgres client (https://github.com/MagicStack/asyncpg). It works very similar to JavaScript, with async and await keywords. I think it should provide speed and comfort especially if you're a regular JavaScript user.


I wrote a pretty substantial experiment using asyncio for 100k websocket client connections across a massive theoretical fleet of robots. It's not open source but the general result was that I used to hate async in Python and now I love it.

The syntax makes blending sync and async easy. And the cooperative multitasking paradigm is very easy to reason about. No locks!


If you did a write-up or went into more detail about your experience, I'd read it.

Big fan of Python and am heavily invested in its ecosystem, but I currently prefer async programming in modern JavaScript compared to Python.

Did you make use of third-party async libraries?


I'm going to ask my boss to open source it and then do a write up.


Python is even better since it has http://trio.rtfd.org which provides structured concurrency.


The author switched to kotlin from clojure, kotlin's interop with java is leagues ahead of clojure.


May I suggest Kawa (https://www.gnu.org/software/kawa) if you're open to Lisp-style syntax (Kawa is a Scheme dialect)? Kawa has great Java interoperability, Java-like performance (with the help of optional type declarations), a nice REPL, full documentation, maturity (continuous development since 1996), and many neat features. It doesn't require special tools or workflow. (I do suggest using the git version, as it is overdue for a release.)


Blog Post Author Here:

I write Clojure, Kotlin, and Scala all on a pretty regular basis. Truthfully, I do write less Clojure now than at the time I wrote this post, but that's predominantly because my current interest (and salary) are centered around Kotlin + Android.

I still write Clojure quite a far amount, but I don't blog about it as much as I'm not "exploring" the language as much. Sometimes, I'll find or create something cool and share it, but most likely that'll be Kotlin/Android related right now.


Kotlin simply is an attempt to fix Scala problems, which in itself is an attempt to fix Java problems. Clojure by design - simply doesn't have those problems.


What Scala problems does Kotlin attempt to fix?


Let's not go into the rabbit hole of "Scala has no problems"


Not my intention. But yours was a bold claim, and I wanted to hear your thoughts.

I do not agree with you. Scala is a mostly functional language, and Kotlin is mostly imperative. How is that fixing Scala? They are two different languages, and Kotlin being newer, is inspired by, among others, Scala.


I don't think that was nattmatt's intention. It sounded like a genuine interested question to me, and I for one would also love to hear someone expound on this topic.


I do not have experience with Kotlin and it's interop with Java. Do you maybe have examples or articles which you'd recommend on the subject?




It sounds like he is doing Android, where indeed Kotlin is strong, and an officially supported language too.


Blog Post Author Here:

That's correct! I did not want to give up Clojure or Scala for Android however! I tried writing an app in both and it was miserable, so I went back to Java. And then I thought -- well, hang on, I'm definitely not going to write this in Java if there's a less-verbose alternative. So I forced myself to learn Kotlin and actually like it quite a bit. In a few ways, it reminds me of when I first worked with Groovy.


Blog Post Author Here:

My opinion has not change as I've never looked back ;)

I've found a fun little home in the JVM, but I'll definitely check out data classes in Python! That seems pretty cool :)


If you allow me to compare my very limited experience with Python (hacking SublimeText plugins) to my rather extended experience writing Java or Ruby (i.e class-based languages that traditionally newline-&-deindent closing parens rather than aggregating them in a trailing tiger tail like in lisps) ...

... I would say writing programs in Clojure leads me to writing 10 to 20 times less code on average.

I recently rewrote 15000 lines of Java (several weeks worth of effort split in several dozen files) down to 500 Clojure lines (in one weekend and one single file).

Anyone else with that kind of experience ? To me this is a tremendous advantage and any argument for code readability is really just argumentation about reading code at a small scale (say one file) that unknowingly makes trade off about reading code at bigger scales (say the readability of a whole project).


> ...the “implied self parameter” on python methods turned me off.

If I had a nickel for every time I got the "TypeError: random_function() takes 1 positional argument but 2 were given" because I forget the "self" parameter I could buy myself a coffee. Probably a fancy one from the Starbucks even!


It's not implied, it's explicit in Python. you have to put it right there in the method signature! And it's obvious when python passes it in cause you used dot syntax!

Other languages have implied "this" or "self".

  class LiterallyThere:
    def method(self, foo):
      "Literally explicit, self"
Dot operator means apply this method to thing left of dot by executing method with thing on left as first arg.

LiterallyThere.method('foo')

passes the class object LitterallyThere as the first arg

lt = LiterallyThere()

lt.method('foo')

passes the instance of the class object represented by lt as the first argument.

method(something, 'foo')

If you were able to reference method without a dot, you would have to supply two arguments cause method takes two arguments.

You literally, explicitly have to tell the interpreter what you are passing as first parameter and you method signature has to literally, explicitly accept that parameter.


I always thought this made sense: a method, considered as a property of a class isn’t yet bound to any particular instance while, considered as a property of an instance has an implicit parameter of the instance you’re currently operating on: languages like Java hide this “this” parameter from you by passing it automatically while python makes it explicit so you can form a better mental model of what’s going on


The problem is that Python is the exception here. Those of us that learnt other languages first already have a mental model and find this explicit self parameter weird. And it's more typing.

Yegge thinks that "self" is a wart and I agree. The article is a fun read but it's showing its age. I suspect Python might just have beaten Perl by now.

https://sites.google.com/site/steveyegge2/tour-de-babel#TOC-...

I suppose Python's way there's one less reserved word. Are there any other advantages?


I also like how you can call the method on the class and pass self in explicitly. e.g.:

    class Foo:
      b = 4
      def foo(self, a):
        return a + self.b

    inst = Foo()
    inst.foo(3)
    Foo.foo(inst, 3)
I haven't written python in a while, but I really like that python makes this possible and obvious and I've found this behavior pretty handy when working in a more functional style.


Also, I find writing languages with an implicit self like Java, Kotlin and Scala really irritating because there's nothing to distinguish access to a locally-scoped variable or function from access to a class member. I've always appreciated how, in Python, `x` always refers to something bound according to Python's pseudo-lexical scoping rules while class members always have a `self.` to make them stand out.

These days, however, I mostly write Common Lisp and Clojure which, each in their own way, have explicit selfs: Common Lisp because it uses multiple dispatch and, consequently, there isn't one "self" and Clojure's protocols are designed to dispatch on the first argument, which is always passed explicitly.


I think the way Lua handles it is much better. If you have object foo of class Foo with method bar, accessing it like foo.bar does no magic on it. You would have to call it as foo.bar(foo, ...) if you wanted to access the method with a dot. Only foo:bar will do the magic on it. If you call it as foo:bar(...) then the object will be passed to the first parameter. This follows "explicit is better than implicit" much better than the implicit self parameter in my opinion.


I'm no Lua expert, but isn't this because Lua's object model is more prototypical inheritance than a standard class-based model like python has?


There are linters for these kinds of things that work with most editors. E.g. VSCode with pylint marks the method immediately, if you forget `self` or `cls`.


Willing to bet money the author switched to something else by 2017. For the overwhelming majority of developers Clojure is this tinker toy we fiddle with for 6 months to a year before getting bored and fed up with its limitations.


I don't have a horse in this race but I find it interesting that in my career I've seen a lot of bad code but the worst clusterfucks I've met where all in Clojure while the community seems to be chock full of purists and in general people that takes craftmanship seriously at least at face value.

¯\_(ツ)_/¯


"The worst injuries I've ever seen were in big factories with large, powerful tools -- not in home garages where people just use hammers and hand-saws."

Powerful abstractions are... powerful. They're more difficult to use correctly, and take longer to learn.

The conclusion shouldn't (always) be to avoid them entirely.


I'd wager good money the exact opposite is true.

Big factories should be better regulated and more professional leading to less accidents. It's the weekend dad with a beer in his hand trying to get something done quickly that saws through their tendon and fucks up their wrist.

Also python is definitely not as simple as a hammer and hand-saw.


Allow me to direct your attention to one of the internet's hidden gems:

https://www.youtube.com/user/USCSB/videos?view=0&sort=p&flow...

Beyond that, these metaphors have gotten too out of hand to have a meaningful conversation :)


If you let an arrogant idiot loose on a 15kw CNC machine, they will without a doubt cause a massive injury to them, and given the power to others. If you let them loose on a pillar drill they might get a scratch and a broken bit.

Of course no company would ever let a newbie loose on a multi million pound CNC machine without a battery of training.

And this is where the engineering part of "software engineering" falls down. Companies rarely provide training, documentation or audit trail for junior devs.

All something required if you were doing real engineering.


A good rule of thumb is the more powerful the language the stronger your justification needs to be for using it:

https://en.m.wikipedia.org/wiki/Rule_of_least_power

LISP is, of course, way more powerful than most languages so it is more susceptible to technical debt.


I've used that quote from Berners-Lee in a presentation before; it's one that I tend to agree with. However, I think you've drawn the wrong conclusion from it.

We want to choose the least powerful language for the job, but the least powerful language is frequently domain specific. Ideally you'd want to develop within a framework that makes it easy to develop restrictive, domain-specific languages.

I'd suggest that Lisp is actually a good basis for this, as S-expressions make it easier to build smaller languages. Clojure in particular seems to be heading in this direction. Cognitect has spec and datomic, and there's been some interesting ideas from Christophe Grand (IIRC) about using relational programming outside the database.


Simplicity matters more than power.


I guess we would have to believe your empirical observations and ignore the facts:

- Clojure has been noted as the most payed language in dev surveys of the past few years

- Again, different surveys (e.g.: stackoverflow, state of javascript) shown that Clojurists overall are more experienced devs. This kinda makes sense - usually people try Clojure out of curiosity and not for dogmatic reasons and not for the points in the resume.

- Someone did a data analysis of github repos for bug density and Clojure was at the top. https://dev.to/danlebrero/the-broken-promise-of-static-typin...

However, I can say the similar thing: I have seen many clusterfucks in my career. And the worst of them I've seen in C#. Why C#? I don't know, probably because I was inexperienced, young and stupid, and anything (even slightly outside of norm) could've interpreted by me as a clusterfuck. I have no reasons to go back to C# and re-evaluate my opinions about it, I'm sure part of me will be forever biased about it.


Pay for a language doesn't reflect skill, or even implied skill. Its simply a function of demand and supply

At $financial-news company, someone decided that they should re-write a few critical parts of the system in closure, partly due to boredom, partly due incompetence. They swanned off, along with 90% of the other people who knew how to maintain closure.

This left a bunch of junior java devs to maintain a ill thought out ego piece.

This logically meant that said company had to spend a large amount of money of contractors to maintain the system whilst they re-wrote everything in the same language.


> Pay for a language doesn't reflect skill, or even implied skill

In this case it sorta does:

a) Clojure is known for having a bit of learning curve and being a Lisp, it rarely attracts newbie programmers

b) it is famous for its concise syntax and for being able to build and maintain things with smaller teams. ROI of 3 Clojure devs is almost always can be predicted to be much higher than of 6-7 Java/Javascript/Python/ Ruby/etc. teams. Pick a few successful Clojure(script) projects and see how big the teams are, they are usually not that big

c) Flexibility of Lisp lets you bend your code for different paradigms: functional, OOP, logic, relational, CSP, reactive, declarative. In my experience, when most other (practical) language acolytes simply ignore academia Haskellers and Clojurists tend to be genuinely interested in CS papers. I think that is due to their experience - again these languages usually attract more seasoned developers


you don't have to believe a damn thing, it's just my experience.


I work with clojure, and love it, but believe the problem is caused by java or equivalent paradigms applied to clojure.

The high probability that clojure was someone's first functional language could also contribute to the problem.

Your thoughts?


This is just blaming people for using Clojure's features though. If deep JVM integration wasn't a selling point, you'd use a vastly more mature Lisp, right?


A lot of us use Clojure despite the JVM, not because of it. Closure has a lot of (other) compelling features.


I mostly write Clojure because, unfortunately, it's nearly impossible to sell management on Common Lisp: ime, Clojure is a nice language, but it's very uneven because it's well-designed for the sorts of things the core team cares about and everything else is barely passed the MVP stage.


No, CL looks quite long in the tooth when compared on ergonomics and ecosystem. It may run faster but otoh the Clojure concurrency story is great. There's also good IDE support for more enterprisey tastes (Cursive) in addition to the phb-scaring Emacs/vi, and ClojureScript is a huge selling point in many fields. The JVM has a lot of technical plus sides aside from the library interop - a lot of tooling such as Newrelic and Maven can be directly leveraged.


The parent post did not say JVM, they said Java paradigms. Vastly different things.

Writing Javaonic Clojure vs Clojuronic Clojure. So, trying to replicate the style/structure/paradigms of Java rather than learning and using the Clojure styles, structures and paradigms.


Yes, I understand, and I contend that being able to do this, and leverage Java libraries bidirectionally in impure Java-y ways is an explicit, deliberate and fundamentally useful feature of Clojure. Unless you're trying to say that things like protocols are fundamentally un-Clojurey because they smell of OO or something in which case I just disagree.


More widely, Clojure's pragmatism and the fact that it's not overly sniffy about what paradigm to use is one of its nicest features. If I wanted my programming language to make me feel bad about myself, then obviously I'd just use Haskell and be done with it.


You may not claim to have a horse in the race, but rhetoric like this doesn't point you as non biased.

Clojure has yet to impress me, but most of the poor code in any language I have dealt with stemmed from people trying to redefine the norms of the language they were using. And attempting to solve problems they would be lucky to grow into.


What a passive aggressive comment.

> At least at face value

Implying that in actually they are what a bunch of incompetent hacks?


That the results didn't match the values. Disconnect happened for reasons I didn't fully understand. On a second observation I must have touched a VERY raw nerve given the huffy reactions.


Let me break it down.

>I don't have a horse in this race but

This is like someone saying I'm not racist but... After which they tell you why they really hate black people. You clearly do have an opinion your statement is a plea for imaginary objectivity.

You follow it up by elevating anecdote to analysis without the work required for real insight. The crack about at least on its face is just designed to offend. The silly emoji after doesn't soften what is basically trolling. If you want to seriously discuss you should start by making substantial points.


I admire your restraint, you stopped short of calling me a nazi at least.

Again, you're completely missing the point: I don't have to "prove" anything to you. I don't even code anymore for that matter, and I can't give two shits if you prefer to write Clojure, Typescript or whatever rocks your boat, that's something that matters to you and not to me.

I simply observed that for a community that spends so much time talking about craftmanship and clean code, the worst messes I witnessed were all Clojure codebases and that's it. If you feel attacked... that's your own insecurity speaking.


You don't have to prove anything but if you do have a negative or positive opinion to share optimally your original comment would indicate that you are willing and able to discuss the matter.

Making a drive by comment and the copping out isn't much of a contribution to the conversation.

Furthermore look at this comment.

> I admire your restraint, you stopped short of calling me a nazi at least.

Some time in your life you should try actually try being an asshole instead of playing one on tv. It would be more honest and satisfying and people that aren't thin skinned can take a little civil disagreement.

All things suck to some degree. You might have an interesting perspective to share on situations where clojure sucks but you wouldn't know it because you are happy to stir up discussion but disinterested in any actual substantial disagreement. Guess that is done then.


Not sure if it's still a thing, but the slowness of starting the REPL was what kept me from moving to Clojure. Python is doing much better in that regard.


`__setup__.py` is not a thing

it's `setup.py`


I switched from Python to Clojure (because abstractions), and then to Go (static types; efficiency) and then to Elixir (because work) and then to Haskell (no more lame types and stupid nil errors).

I feel like with Haskell I have basically achieved perfection, and I'm more prepared to work on the other languages if the situation calls for it.


"Why I Switched from Python to [Shiny Newer Language]" seems to be the thing on HN this week.


The really hilarious part about this one is that it’s from 2016, and the author seem to have since switched from Clojure to Kotlin.

I think it’s still interesting to read about people switching. I mean, I’ll never understand why people prefer functional languages to OOP. I think it’s just so much easier to maintain OOP in the long run, and you can frankly build most of the advantages of functional languages within OOP if you need to. But then you read an article like this and you get a reminder that freedom can lead to some really horrible stuff and that is especially true for Python in general.

I don’t see this as “we should all switch from Python” sort of thing, but rather a “don’t do these things with Python”.


I write Clojure, Kotlin, and Scala all on a pretty regular basis. Truthfully, I do write less Clojure now than at the time I wrote this post, but that's predominantly because my current interest (and salary) are centered around Kotlin + Android.

I still write Clojure quite a far amount, but I don't blog about it as much as I'm not "exploring" the language as much. Sometimes, I'll find or create something cool and share it, but most likely that'll be Kotlin/Android related right now.


I don’t agree that Clojure is a new and shiny thing.


Clojure itself is not a new, shiny thing. What's new and shiny is "I switched from Python to a language that will make me write more LOC."


LOL. Clojure is actually one of languages with a terse and minimal syntax, pragmatisized to its core.




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

Search: