Hacker News new | ask | show | jobs
by romero-jk 1694 days ago
What do most here use Clojure for? For me, there was little value in using it over nodejs for single server CRUD apps with postgres. Some things I liked are namespace keywords, REPl integrated with your editor, immutability. But still, it felt like swimming against the current. Immutability is not as useful in a single-threaded event-loop like nodejs. No third party SDKs, even though I know Java using the Java SDKs was tedious labor, most Clojure wrappers are pretty bad too, so more interop.
6 comments

I've been a Clojure hobbyist for many years, and these are a few of the things I've done repeatedly:

- Scrape webpages with http-kit and Specter

- Visualizations with Quil

- Serve static pages with Ring, Compojure, and Hiccup

(un)Fortunately, I don't write Clojure professionally, so I can get away with liking it for aesthetic reasons as much as anything. It definitely does feel like swimming against the current sometimes, though - one runs into bus factor issues a lot in the Clojure ecosystem. But hell, I love it all the same :)

Personally, I am really excited for Clerk - this looks like a really useful tool and I'm looking forward to trying it out!

Got any links to stuff you’ve done with Quil?
Yeah, here's one I put together a few years back: http://pontingdynamics.org/tri
> Immutability is not as useful in a single-threaded event-loop like nodejs.

Concurrency is not the only, or even primary, benefit of immutability in software. State and state management is the number one driver of complexity in software, and complexity itself is the number one problem that makes building software hard. [0]

> For me, there was little value in using it over nodejs for single server CRUD apps with postgres.

You can use clojure on nodejs with the value that clojure is a much, much better language than javascript.

[0] http://curtclifton.net/papers/MoseleyMarks06a.pdf

I was referring to Clojure's immutable data structures, not immutability in general. My nodejs apps are just transformations pipelines of pure functions with postgres being my global mutable state. Clojure doesn't offer me anything here that I can't easily do with JS/node.

Sure Clojure is a better language but not better enough to justify the additional abstraction layer IMO.

I sorely miss Clojure's immutable data structures when I use other languages (Python, Javascript, C++). They give me a guarantee that nobody else is going to be changing the data, somewhere deep down the call stack. I've had cases in production Python code where some seemingly unrelated code module somewhere was mutating data without my knowledge, breaking whatever I was working on. This is much harder to have happen in Clojure (you have to go out of your way to store things in mutable types, or use Java interop hacks to do this, which shouldn't pass code review without good reason).

Clojure's sequence abstraction also leads to a very rich and powerful set of core functions that make transforming your data a breeze. Other languages can do this too, but in Clojure I find its just really easy and natural since its part of the language's core library.

It also helps emphasize pure functions: data in, data out. Which makes my code much nicer.

Besides that, I use clojure because I like reitit and spec/malli for writing my HTTP routes, and hugsql for writing my SQL, both much better than the equivalent libraries in other languages I use.

I also like its emphasis on data first, code second. Sure you can do this in other languages too, but Clojure encourages it since its something the community has rallied behind.

They give me a guarantee that nobody else is going to be changing the data, somewhere deep down the call stack. I've had cases in production Python code where some seemingly unrelated code module somewhere was mutating data without my knowledge, breaking whatever I was working on.

Since we are talking about single-threaded single stack context, immutability wouldn't have saved you here, even with immutability you could have receive the wrong value/data from some function in another module down the call stack. The problem was not mutation but a function returning bad output.

You are implying some code modified data underneath you, which can't happen in a single thread.

This was in a single-threaded application. Immutability would have saved me because it guarantees that no nested function call can mutate data unless its passed in or stored in a mutable way (in a Clojure atom, for example). If the data structure does not contain any mutable constructs, then it cannot be mutated in a way that is visible.

> You are implying some code modified data underneath you, which can't happen in a single thread.

Sure it can. I mean, yes, the code is ultimately called by my code, through some deep transitive dependency, but the point is that if you have a complex architecture where dependencies interact in complex ways, things can be mutated by a nested function somewhere deep in the call stack. Immutable data would prevent this, unless the data is stored in a mutable object, but in that case, its easy to audit your mutable objects to see where they are used, accessed and mutated. In a language without immutable types, any assignment operator could potentially be mutating something that you don't want it to mutate.

Besides, many otherwise-single-threaded applications we develop these days do have ways that things can mutate asynchronously: request handlers, timeouts, animation frames, network response callbacks etc. It doesn't literally have to mutate them in parallel with other code I'm executing for it to become a problem, just when you flatten a particular execution chain the data may have changed without your knowledge between two steps. For example: I do a network request to an API and wait for the response, has the data been mutated when the response callback is run?

It may even be perfectly valid to do so, my main concern is that it should be explicit and carefully managed. If data defaults to immutable and mutable data requires some extra ceremony (ie deliberate intent by choosing to use an atom or other mutable type), then this signals where care must be taken and makes it clear where integrity may need to be checked, data may need to be reloaded or recalculated etc. Everything else is guaranteed to have the same value you left it with, no matter what, even if it was passed by reference to a callback or deeply nested function call.

Anyway, I was just pointing out why I miss Clojure's data structures when I use other languages and in what cases Clojure would have saved me from headaches. YMMV and all that.

An aspect of immutability I really appreciate is that I know no variable has been changed after it's been assigned to, including by me. If I want to know where the value was assigned, I only have to look for the only place it could have been assigned. I don't have to search anywhere else.
99% of variables are local to your function, is not like program a thousand of global variables.
> Sure Clojure is a better language but not better enough to justify the additional abstraction layer IMO

Speaks for yourself, I'd take a pay cut to be able to use Clojure over JS (don't tell anyone )

Some of the why that is is captured here: https://gist.github.com/didibus/6e5ff960b25fdc1a5f8f97acede6...

But understand that a lot of it is about what language you personally enjoy and prefer, Clojure makes programming 10x more fun for me, and I already am someone who finds programming in general fun.

If you're using pure functions then you'd likely profit from clojure's data structures, as they're much cheaper to copy and modify than javascript's native structures. Of course you could use something like ImmutableJS or Immer, but then you're putting yourself at odds with the rest of the JS ecosystem and built-in functions and you have to transform values back and forth. This gets a lot easier with Clojure(script) (even though there's obviously still some casting back and forth when you're going outside the CLJs ecosystem)
You can use Clojure on Nodejs too.

I personally can’t use it for production code. But whenever I automate something or write some prototype I use Clojure. Iterations are faster, more direct, less of a hassle.

There has also been someone commenting around here that they use it as an network repl to poke around running production systems, which I find an interesting use case.

> What do most here use Clojure for?

That's to evade having to use Java, Scala or Kotlin.

Clojurescript: because it's the easiest (compared to Elm, Purescript and Rescript) to call JS.

I use it for everything.

Professionally, I write backend services (mix of micro-services and bigger SOA ones) of various types. Some command lines, some scripts, a few web apps.

In my spare time I use it for random things, mostly stuff I mess on, personal scripts, doing programming challenges, websites, data exploration, etc.

You can use Clojure with NodeJS as well using ClojureScript by the way, or NBB if you want something more straightforward for writing NodeJS scripts.

It's a good default app dev language for web backend and frontend projects at work, and attracts good programmers to the team(s).

Immutability benefits are first and foremost about semantics in most cases.