Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What is mutable by default in Clojure? Nothing. You need to explicitly declare mutable variables with a special notation. Are you talking about the underlying VM? Yes, the Java API are mutable, not surprise here, it's not Haskell.

Imperative? What is imperative by default in Clojure? Nothing. It's one of the least imperative Lisp, favoring functional constructs all over the place unlike Common Lisp or even Scheme.

Blocking IO. Ok. So you want JavaScript? You have async i/o lib for Clojure and the primitive of the language to create threads are relatively good (relative because the limitations come from the Java VM).

Zero guarantees at compile time. Almost true. It' s a dynamic language and does not pretend to be something else. It has pros and cons. Like modelling a domain is very simple since you almost always reuse the same datastructure and having ubiquious immutable datastructures build a solid foundation for an ecosystem of libraries.



> What is mutable by default in Clojure? Nothing.

All data in Clojure inherits from java.lang.Object. I get what you’re saying, but in real life commercial projects, especially when you use libraries and Java classes, the code ends up with a lot of mutable things. You can never know if any function you call does mutate something without keeping all definitions in your head, and with 20+ people on the project, this is a challenge. Programmers of languages less sexy use frameworks to manage state and effects, but not in FP, which clojurians think they do.

> Imperative? What is imperative by default in Clojure? Nothing.

Please note that I’m not praising Common Lisp or Racket either - the same criticisms apply to them, except they ship with type systems. In Clojure IO is usually done by calling a function that does some (blocking) effect and returns something. That is programming in imperative style. In practice effectful functions find their way into programs, and taints everything referring to it. For this reason, clojure programs written by teams tend not to be as FP.

> Blocking IO. Ok. So you want JavaScript?

I for sure prefer to write a backend where no teammates ever introduce blocking calls. I once worked on a webpage which took minutes to render, because of blocking calls on the back end. It was a mess, after having been worked on for years by around 70 coders, including consultants for the biggest names in Clojurespace.

> Zero guarantees at compile time. Almost true.

Except for syntax errors.

These things turned me off: imperative and blocking IO and effects and no square framework to contain it, combined with zero compile time checks. It results in teams making bespoke solutions that other teams in the company (or even consultants from the famous players in the Clojure space) solving their code needs by meeting with the authors and asking for changes, having a ton of testing to make sure it does not fail in rare code paths, and having one guy on every team religiously adding specs in order to not go mad.


> I for sure prefer to write a backend where no teammates ever introduce blocking calls.

Please note that forcing an execution model in the language will inadvertently cause headaches for some kinds of domains. Since clojure makes no wild choices the language can be ported to other runtimes (.net, js) and benefit from future runtime improvements without breaking the language (JVM virtual threads, value types).

Since the core is small and lisps are extensible, we can have a multitude of choices in libraries. So if you really like typy things you can check out core.typed, spec, schema, malli etc. If you want async you can check out manifold, promesa, core.async etc. If you want non-blocking effects handling you can check out missionary or darkleaf/effect.


Language design is about what features not to include. If you include blocking IO, or allow unsound programs, disallow GC-less allocations, you are forcing a model of programming onto the user, which is a good thing. Node.js became a hit because it lacked the feature of blocking IO.

Having many choices available, and teams of maybe 50-100 good mates coming and going, and a period of 5-7 years, has a high likelihood of producing chaos code.


I agree, constraints often bring benefits. The question for a language is which ones to bring in and which ones to leave open. From that perspective I value clojure's choices (and don't value js every-io-is-a-callback design).

Any project of the size you mention needs technical leadership and constrained choices (for IO we use this, we do http this way..). This will be true regardless of what language you're using.


Inheritance from java.lang.Object does not make Clojure data mutable. Blocking for IO does not make Clojure imperative, it not a purely functional language, but its still a functional, not imperative, language.

It's possible to build a service in a purely functional language using non blocking IO that also takes minutes to render. No language will save you from a bad design.

I do agree that Clojure is not the most suitable language for large projects with lots of contributers because of it's dynamic nature.


The problem is not the built-in Clojure data structures. The problem is that a huge part of Clojure's heavily marketed value proposition is seamless-ish Java interop, which means working with Java objects and all their associated mutability. So while pure Clojure has very controlled mutability, pragmatic Clojure inherits all of Java's problems. And since they hype interop so much, nobody wants to rewrite functionality in Clojure when they could just leverage Java.


In practice all the interop lives at the edges of the application. As a concrete example, Pedestal HTTP server has around 18,000 lines of code, and 96% of it is pure functions. All the IO and side effects are encapsulated in the remaining 4% of the code. This has been a common scenario for the vast majority of Clojure programs I've worked on in the past decade.

https://www.youtube.com/watch?v=0if71HOyVjY

Core of the application does data transformations using native Clojure data structures. This is where all your interesting business logic lives. The interop lives at the edges and typically used for stuff like database drivers, HTTP servers, file access, and so on.


Thank you for these comments they are super interesting to me as a relatively new Clojure programmer.

Can I ask two related questions -

1. Reading between the lines it sounds like the mutability issue arises from interop, both in the core code and any dependencies that wrap Java?

2. If yes, curious why did the folks involved did not write native Clojure libraries to eliminate the (worst?) blocking dependencies (or patch the Clojure wrapper libs)?

I ask 2 because my understanding is interop was partly a bootstrap strategy for the language and there was always (I think?) an assumption that the native Clojure library ecosystem would grow and make interop less necessary. Is that not happening enough?


The whole point of running inside another ecosystem is to reuse existing solutions. Many wrappers hide mutability and provide an immutable API. And more java libraries are being written with immutable classes.

I also failed to understand the "everything is a java.lang.Object" sentence. It doesn't make a clojure map mutable. In the end it's all just ones and zeroes, but that misses the point.


>The whole point of running inside another ecosystem is to reuse existing solutions

Ya totally, but I think OP was hinting at a fundamental tension between this benefit of interop and the desire to be functional where Java is (generally) not. Clojure lets you reuse the wonderfully huge library of Java solutions -- but those solutions are (generally) not written with immutability in mind, which I could see becoming an issue when you're trying to write a concurrent app. I suspect that's what the java.lang.Object reference was about, although obviously I'm not sure.

>Many wrappers hide mutability and provide an immutable API. And more java libraries are being written with immutable classes.

All that sounds encouraging....




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

Search: