Hacker Newsnew | past | comments | ask | show | jobs | submit | midnight_eclair's commentslogin

> I assume you need Woke and rubbing "pride" or "Ukraine" into everything

wow, what a self report


lazy so copying from a different thread:

you might be drinking some of that AI koolaid, conflating our suddenly hypertrophied abilities to produce code regardless of our familiarity with the syntax or the APIs with ability to produce and deliver good quality products, but this delusion is getting reality check as we speak.

a realization is propagating through the industry that being able to produce more code than you're able to review, comprehend and internalize is actually not a great thing.

https://news.ycombinator.com/item?id=48381598

that said im not anti ai, i just think it is being applied in the most moronic ways during this hype cycle (gary tan anybody?)


because earth's geosync orbit is at 36k kilometers (function of gravitational force and rotation speed)

> `(let [a b] ...)` instead of `(let (a b) ...)` is _not_ okay

it is however quite consistent, clojure uses vector form for most macros that require a "control" form before a "data" form: argument list in defn, names list in let, iteration descriptors in for, etc. you get used to consistency quite easily.


> the real value-add is in the runtime, not the syntax. Java has a solid runtime but it's not yet as good as Erlang's, maybe even not up to the standards of Golang

won't lie, this is hilarious. you got me from nodding along to being the spitting out food meme guy in a span of couple seconds.

JVM runtime is undeniably the most well researched and optimized runtime in history of runtimes, specifically in realm of concurrency and parallelism, it literally carries like half the world on it's back.

not to throw any shade on erlang vm - i've been a fan for well more than a decade, but other than making some interesting, but limited in practice, tradeoffs with regard to concurrency architecture, it doesn't really offer much more.

go's runtime is just a different beast altogether designed with different goals in mind and with no baggage of backward compatibility with legacy.

one particular detail i'm very grateful to Clojure for, is exactly the ability to use JVM runtime without having to touch any Java.

> Programming language syntax scarcely matters

on the contrary, it matters quite a lot.

you might be drinking some of that AI koolaid, conflating our suddenly hypertrophied abilities to produce code regardless of our familiarity with the syntax or the APIs with ability to produce and deliver good quality products, but this delusion is getting reality check as we speak.

a realization is propagating through the industry that being able to produce more code than you're able to review, comprehend and internalize is actually not a great thing.

and that's where syntax matters - it has to be high signal/noise, it has to expose you to right abstractions and it has to be pliable to allow the codebase reflect the problem in a way that minimizes cognitive load both during production and during consumption.

LLMs are language models and syntax is a crucial part of any language.


LLM bashing aside (although I tend to agree), I agree with midnight_eclair. The claim that Erlang or Go are outright superior to the JVM doesn't really stand up. They're better at some things, and worse than others.

Regarding language syntax, it definitely matters. In the same way the vocabulary we use shapes our thoughts, the expression of a programming language shapes the implementation. Of course, as Clojurists know all too well, it's entirely possible to write Java in any language!


I don't think you, I, him and others necessarily disagree at all here, it's just that living language has defects and I can't spend 30 minutes clarifying beforehand like I am doing a math proof.

To me the strengths of Erlang BEAM VM and Golang's runtime nullify their weaknesses (and of course they do have weaknesses, some are pretty hard to swallow too). To me they sit on the positive side of "right tool for the job".

I just can't work with global mutability anymore. It's an endless hopeless pit of determinism bugs. I picked my battles. Respect to whoever wants to make a career out of chasing them but that's no longer me. I want to make measurable business progress when I work and not babysit defects that should have stopped existing two decades ago.

I can agree with other posters that the JVM has come a long way. I might reassess if I get the time. And I am not bashing on anything here. I am saying what my experience showed me. To me it's tiring to pretend that all languages and runtimes are equal and I'll keep claiming they are not.

As mentioned above: I don't think we necessarily disagree at all.


Well, you are kind of using my comment to vent your frustrations about AI while it has barely anything to do with it -- but you tried to link the two, unsuccessfully. Which is not fair as you have no clue of my stance on AI and are extrapolating a bit too much.

Syntax does not matter simply because it's an extremely leaky abstraction of the runtime below, is my point.

Of course syntax must be high signal/noise ratio, I believe every reasonable programmer will agree. But many are making entire careers in PLs where that's not the case. Hence, in practice it does not seem to matter much, for the better or the worse.

RE: runtime, try and pay attention to the parameters given in my comments. I specifically acknowledged that the JVM is a great and mature runtime but it's lagging behind on STM / actor capabilities. Tearing down a straw man is not impressive and it comes across as you trying to gain visibility by deliberately misrepresenting your discussion opponent's arguments.


> you have no clue of my stance on AI and are extrapolating a bit too much

apologies, but maybe next time try to elaborate more on sweeping statements like "syntax doesn't matter", because in current context my assumption for why you would say that is not all that outrageous.

> Syntax does not matter simply because it's an extremely leaky abstraction of the runtime below, is my point.

that would be the reason why syntax does matter, wouldn't it? nobody wants leaky abstractions!

ironically, Clojure is a great example of a hosted language that does not leak much in terms of underlying runtime, as evidenced by the fact that it has been implemented on top of a variety of runtimes with decent control over cross-runtime code reuse.

> acknowledged that the JVM is a great and mature runtime but it's lagging behind on STM / actor capabilities

you're stating this as if it's a fact, but what is your evidence? afaik jvm has a very extensive actor model library (Akka) and clojure does include a solid STM implementation (https://clojure.org/reference/refs).

the reality is that both of these approaches to concurrency are simply not popular enough, so your grievances with JVM for (allegedly!) lacking some important features relevant to them are not in sync with the demand.

> Tearing down a straw man is not impressive and it comes across as you trying to gain visibility by deliberately misrepresenting your discussion opponent's arguments.

don't debate-bro me bro, there are no straw men and no misrepresentations of your messages. if there are invalid assumptions - it's because instead of turning this into a dozen-messages-deep interrogation of what you really meant, i'm taking shortcuts and assuming what i believe is most plausible interpretation.


> that would be the reason why syntax does matter, wouldn't it? nobody wants leaky abstractions!

Well I thought we were describing our current reality, not our _desired_ one? Yes nobody wants leaky abstractions and yes they are everywhere.

Syntax matters insofar as to discourage bad habits, is what I'd refine from my previous statements. Most programmers go for the default so defaults and syntax that steers you the right way to think and write matter a lot.

That being said, people write FP Rust (myself included) and have plethora of JS libraries where immutability and FP patterns are the default. Which is a sad state of affairs but much better than nothing -- as it's introducing programmers to immutability and FP and they otherwise would never know.

> as evidenced by the fact that it has been implemented on top of a variety of runtimes with decent control over cross-runtime code reuse.

That was my top 1 reason to try it btw; I was intrigued by the fact that people are interested in making it work universally in at least two very different runtimes. To me that signals good language design and good architecture. Which I already knew; Clojure and Racket are amazing on their own.

> you're stating this as if it's a fact, but what is your evidence? afaik jvm has a very extensive actor model library (Akka) and clojure does include a solid STM implementation (https://clojure.org/reference/refs).

As already said multiple times in the thread -- my info is stale (as claimed by multiple posters).

That being said, has Akka started making full use of JVM's new green threads? Has Java itself started introducing immutability and STM / share-nothing as first-class citizens? If not, then by the "programmers reach for the defaults first" rule above I'd think Java is not yet ready. OK Clojure has these amazing libraries, kudos. Has anybody rolled up their sleeves and said "Alright, BEAM VM's reign is over, I am making the same or better runtime as them in Java / Clojure!"? If not, I'll not yet revisit.

I just don't want to deal with the endless pit of determinism bugs that global mutability nets us. The gift that keeps giving.

If Akka / Golang's runtime / Rust's various actor-emulating libraries catch up to the OTP, I'll very likely drop Erlang/Elixir because it's a struggle to have a good stable employment (or even contracting lately) with them.

Even if the BEAM VM is slower and has a few annoying sharp edges, its strengths nullify its weaknesses due to the nature of my work (HA web / API servers and also API gateways and orchestrators).


> That being said, has Akka started making full use of JVM's new green threads? Has Java itself started introducing immutability and STM / share-nothing as first-class citizens? If not, then by the "programmers reach for the defaults first" rule above I'd think Java is not yet ready.

> OK Clojure has these amazing libraries, kudos. Has anybody rolled up their sleeves and said "Alright, BEAM VM's reign is over, I am making the same or better runtime as them in Java / Clojure!"?

will akka use green threads? i'm sure it will when the developers behind it deem them useful.

will jvm add immutability, stm and share-nothing primitives that (i assume you allege) are missing? sure, i guess, when it becomes frequent enough ask.

you make it seem as if the world of software development is in some constant battle for supremacy, but it just isn't.

there is no "BEAM VM's reign". and if there was - it's unlikely that anybody would care to topple it. people just want to get their job done and they use whatever tools are available, familiar and convenient for them.

> I just don't want to deal with the endless pit of determinism bugs that global mutability nets us

and a lot of people agree with you. and there's a lot of tools that address that. and i can assure you, when working with clojure, even on the blasphemous mutable jvm runtime, that class of bugs is non-existent.


Well, I don't think you and I disagree on the premises, with the exception of you believing that I make some outrageous general claims -- which I did not.

> you make it seem as if the world of software development is in some constant battle for supremacy

I could have misinterpreted other people in the past -- very possible. But I also stopped caring about "battles for supremacy" a long time ago and at this point in life and career I simply use my experience and brain to go where my work is more productive, deterministic and fulfilling. To me immutability, share-nothing actors, strong vertical scalability (where a lot of PLs and runtimes do well, not only my favorites) and DX (like live prod REPLs and generally trivial observability) are those axii along which I thrive.

You and a few others seem to have emotional reactions to this which, I assure you, are very unnecessary. I ain't threatening neither your livelihood nor preferences.


> That being said, has Akka started making full use of JVM's new green threads? Has Java itself started introducing immutability and STM / share-nothing as first-class citizens?

Amazing how it doesn't even cross your mind that there are trade-offs to those choices. Green threads are awesome, but guess what, they come at a cost. Same for share-nothing semantics.

> Has anybody rolled up their sleeves and said "Alright, BEAM VM's reign is over, I am making the same or better runtime as them in Java / Clojure!"?

You are again presupposing the BEAM has an absolute superiority over the JVM. "Better runtime" makes no sense on its own. Better is always relative to something. Better for whom? For what?

I'd bet that you work on a traditional CRUD enterprise software, and that IO(the database) is the real bottleneck of your app. In that case, sure, the BEAM is a solid choice(so is Python, Ruby and PHP nowadays). But let's please not pretend that is all there is to software engineering.


Any good reason for your rude tone? If we are going to invoke the what crossed somebody's mind trope, I'd lead with that when talking to you -- did it cross your mind to speak calmly and not assume something "did not cross" somebody's mind?

RE: your other similarly rude comment, I have not "appealed to authority" anywhere. I said that I have used multiple PLs / runtimes and made an informed choice... for me. I don't intend to add "...for me" after each sentence. It's redundant and obviously implied when it comes to tech because obviously people have made well-working prod systems with combinations of bash and Perl ages ago. So obviously people can make nearly everything work.

If you don't intend to discuss out of position of curiosity but want to jump on people then I am not interested.


Your first comment assumed I was "speed-running to a conclusion and squinting too hard", and this was "similar to the weird childish name-calling". I think that is in the same area(or worse) than saying "Amazing how X didn't even cross your mind". And sorry, but invoking that you have experience with X, Y, Z and thus your opinion is informed after criticizing some technology IS an appeal to authority.

> If you don't intend to discuss out of position of curiosity

I'm not the one making sweeping statements on the superiority of one piece of technology. Reading your other response, I think you are the one who have little to no curiosity in understanding how you might be wrong.


I have been on the other side as well i.e. the Java / JVM. Hence: "informed".

Your negative assumptions are tiring. Sorry that you got offended by what you quoted (and I said) but I'll drop here.


from experience, during bursts it's never actual web/api server that is bogged down, it's the downstream io bottlenecks.

if your accepting layer is abstracted away and implemented correctly, there is very little performance difference between different concurrency approaches and all you're exposed to as developer is implementation of your handler functions.


Not the case; good abstractions are valuable, but the performance differences between runtimes are very real.

Take the example of some simple HTTP<->blob store service gets slammed with millions of requests when someone using the API does a backfill via some framework on their end that aggressively scales request volume up and out.

Something like, say, async Python/starlette with a coroutine per request is gonna perform slightly worse than Erlang, which in turn is gonna perform much worse than Go.

You're right that those differences are sometimes marginal when the latency of whatever IO the backend's doing dominates the equation. However, in my experience huge volume surges show issues with the runtime (the thing managing/launching multiplexed request handler routines) or the ecosystem (the backend IO libraries' ability to work with the runtime's IO multiplexing and make things like request coalescing easy or automatic) more often than you'd think.

It really takes surprisingly little volume to cripple a return-hello-world Phoenix app that indirects the "hello world" behind way too much middleware and message passing; it takes even less to kick over, say, a Gunicorn instance returning "hello world" at the bottom of the Django middleware stack. Golang with Gin, on the other hand, is surprisingly hard to cripple in the same way. And I say that as someone who likes Elixir and Python a lot more than I like Go!


Thank you. As a guy who made a career out of Elixir (and begins to regret it recently but oh well) I agree that Elixir's throughput is not amazing. However, it can get very far and we should always optimize for the most common usages.

I've personally rewritten one hobby and one professional projects from Elixir to Golang and loved the result; as you said, extremely difficult to bring down a Golang service to its knees.

One clarification: Phoenix server behind Caddy/nginx fairs better btw. But, details. Your point stands.

I am yet to see a Rust web/API service I wrote to _ever_ buckle under pressure and just crash. It was either an application bug (like the famous Cloudflare's `.unwrap()` error from the last weeks/months) or the Linux OOM killer. Literally never crashed. But I did witness it brutally murder a MySQL cluster because it couldn't serve it fast enough. That was both fun and terrifying to watch on the dashboards.


> I did witness it brutally murder a MySQL cluster because it couldn't serve it fast enough. That was both fun and terrifying to watch on the dashboards.

Haha yep. In my experience, everyone running CGI/process-per-request application servers is bullish on switching to a concurrent or cooperative runtime...until they realize they just removed the primary ratelimiter on downstream DB/service accesses.

The converse war stories are also amusing: people rewrite their whole app in a concurrent/asynchronous framework and nothing changes, because the DB driver is still farming out all queries to a tiny fixed-size threadpool of connections that was the bottleneck all along.


Oh yeah, definitely. If your DB server (or any storage backend) cannot have like 200+ connections alive at all times then it's absolutely pointless rewriting your app in Elixir or Golang. You'll just serve DB timeouts in your responses.

> You're right that those differences are sometimes marginal when the latency of whatever IO the backend's doing dominates the equation. However, in my experience huge volume surges show issues with the runtime (the thing managing/launching multiplexed request handler routines) or the ecosystem (the backend IO libraries' ability to work with the runtime's IO multiplexing and make things like request coalescing easy or automatic) more often than you'd think.

fair enough, although at this point we start talking about LB in front of the thing, consumption mechanics, autoscaling signals

i will still maintain that my simple advice for a dev worrying about scale, is that they should focus their efforts on ensuring downstream IO doesn't get overwhelmed (db read replicas, caching, etc) before optimizing runtime performance or autoscaling out unnecessarily.


> focus their efforts on ensuring downstream IO doesn't get overwhelmed (db read replicas, caching, etc) before optimizing runtime performance or autoscaling out unnecessarily.

All good advice, but the choice of runtime can affect the point at which autoscaling and load balancing even need to enter the conversation at all. Optimizing, say, a mostly in-memory cache service and writing it in Golang may yield results like "we can run a single instance of this and serve three orders of magnitude of business growth; slap it behind a DNSRR or a k8s NodePort for update/replacement/fast failover if it crashes, no complex load balancer needed", where writing the same thing in, say, PHP might require discussing orchestration/load balancing/memory/worker process recycling/autoscaling early on in the service's lifetime. Being able to skip those conversations (entirely or for a long time) is a very significant business benefit.


(Upvoted for a really relevant and valuable refinement to the thread.)

Admittedly that layer is almost always abstracted i.e. AWS / GCP and various other smaller hosted solutions that handle a good chunk of load balancing for us. In that landscape BEAM VM's strengths shine even brighter. I've seen firsthand that you can in fact bring a BEAM VM to its knees if you expose it just like that to the net. It's not pretty. Golang fares a touch better and Rust seems almost immune (provided one does not screw up their caching layer and don't do elementary N+1 query mistakes).


this release is about exposing the host language primitives to clojurescript

core.async isn't going anywhere, if async/await works better than promise based implementation, core.async will get an update in it's .cljs parts


fun fact: clojurescript had support for asynchronous paradigm through core.async library (CSP style) long before async/await landed in javascript itself.

edit: i'm in no way trying to diminish the value of this release, just pointing out how cool it is that you can get new language features before they are available in the host language by just adding a library to your dependencies. clojure is awesome!


> fun fact: clojurescript had support for asynchronous paradigm through core.async library (CSP style) long before async/await landed in javascript itself.

Definitely. I was heavily using it and it worked: a few quirks but we did have async/await since more than a decade. I think I discovered it after watching a talk by David Nolen.

Since then I moved to minimal JavaScript on the front-end: SSE is one-way and that is beautiful. I'm glad to see many devs, from a great many different languages, now getting interested in SSE.

Here's a great, recent, talk by David Nolen called "A ClojureScript Survival Kit":

https://youtu.be/BeE00vGC36E

I cannot thanks David "Swannodette" Nolen enough for all the work he did on ClojureScript (and core.async) since its inception. And what's amazing in this talk is that he's actually excited at the idea that we may do away with ClojureScript and use pure Clojure (on the server-side) and server-side events, with just a tiny of JavaScript.

The real demo starts around 26:30. He shows a Webapp running on the client and how much resources it's using, then he shows the exact same Webapp running on the server and pushed one-way to the client using SSE. It is wild: resources usage drops to near zero.

YMMV but I find it easier to reason about my webapps and manage state now that I'm using a minimal DOM morphing lib: I used to have two REPLs (one for Clojure, one for ClojureScript) and lots of back-and-forth traffic and hard-to-track / reproduce state. Now everything is definitely snappier and way easier to reproduce.

I'm not saying SSE is going to work in every case though: YMMV. But in any case the video or at the very least the demo starting @ 26:30 is very much worth watching.


thanks for the link, i was just recently trying to find that talk!

re server-side rendered fragments - htmx is extremely easy to integrate with your clojure(script) projects, i found it to be quite pleasant paradigm, but i have no skin in the game so take it with grain of salt xD


True, but there are many reasons to avoid core.async, especially in 2026.

It balloons up the Js artifact, has no inherent error model, and transforms into state machine code that's hard to read/debug if something goes wrong. Plus, the `go` macro encourages overly-large functions, because it can't transform code outside its own sexpr.

As one Cognitect put it, "core.async is beautiful nonsense".


> has no inherent error model

I'll pitch in here, as I've been doing a lot of thinking about this issue and ended up writing my own (tiny) tools for handling anomalies, modeled on the very well thought-out https://github.com/cognitect-labs/anomalies categorization.

This is actually a much wider problem and not specific to core.async. Handling anomalies is difficult. It used to be that you would have exceptions and errors which would be thrown, unwinding the stack. This pattern no longer works in asynchronous code, or code that needs to pass anomalies between the server and the client. In practical applications, an anomaly might need to be returned from a function, passed through a `core.async` channel, then thrown, unwinding the stack on the server side, then caught and passed to the client side over a WebSocket, and then displayed to the user there.

Solving this well is not easy. I think my toolkit, iterated and improved over the years, is close to what I need. But I'm pretty sure it wouldn't handle all the real-world use cases yet.

But again, this is not specific to core.async in any way.


What is your opinion on farolero[0]?

[0]: https://github.com/IGJoshua/farolero


At a first glance, it does much more than what I would want to.

My status toolkit just extends the Cognitect anomalies to be statuses, adding ::failed (parameters correct, but could not perform request), ::ok, ::accepted and ::in-progress. It also adds a bunch of utility functions like status/?! (throws the parameter if it's anomaly, returns the parameter otherwise) and macros like status/-?> (threads if an expression is not an anomaly). That's it.

I deliberately avoid trying to do too much here.


All async systems have pitfalls, I'd say core.async's are pretty minor compared to most other systems. You're right that `go` can encourage bloated functions and it would be better if it, for example, handled exception propagation (I would guess every serious core.async user has written their own go-but-with-exception-handing macro, it's not hard but it is unfortunate duplication of effort).

(I've never had to think about the state machine code when debugging and I've done a lot of core async debugging. That part really does seem to just work.)


To be more clear, I didn't mean debugging the generated state machine itself.

What I meant was, the use of the go state machine renders certain debugging techniques useless. E.g., stacktraces are less helpful, and js-debugger is pointless, since you can't guarantee the (js-debugger) will get grouped with the state you're trying to debug.

Frequently print/tap is sufficient, but core.async/go narrows your options.


And here I thought I was too dumb to grok core.async all those years ago (ps I still am)


For me it also lacks observability. It has been a few years since I last used Clojure, but I found manifold to be a much better fit for actual production code that you want to optimize.

I loved ztellman’s “everything must flow” talk on the topic.


Heh, I used to maintain manifold/aleph for a few years after Zach left the Clojure community.


That means we have had various exchanges in the past, but I’m operating under a different username here.

I’ve had several PRs accepted into there, I think manifold got a lot of things right, if only it weren’t for Zach leaving the community.

Unfortunately I left the community for similar reasons, I have a different vision of how the language should evolve than the people in charge, but wasn’t as vocal about it. I suspect there are more people like me.

It’s fine, like Rich Hickey said, it’s his project and we have no right to expect anything.

I am forever glad for what Clojure taught me, it made me a much better developer.


> Unfortunately I left the community for similar reasons

AFAIK Zach was just offered well-paying job that was mostly about dealing with Scala, it wasn't about "how the language should evolve"...


Zach wanted to introduce a community-driven steering community, make the language easier for beginners to adopt, and standardize library choices.

This did not align with Cognitect’s centralized stewardship.

This was around the time that there was some quite some commotion in the community around this, with Rich posting his infamous “Open source is not about you” conclusive post, which was in direct response to @cemerick’s Twitter post: https://x.com/cemerick/status/1067111260611850240


That was in nov 2018. Two months later Tellman published his "Elements of Clojure". I don't remember the date when he retired from the community. And I don't remember him publicly saying anything about that drama, but I do know for a fact that he joined a team with a ton Scala. How do I know that? Not sure, he might have told someone I know. I might have heard that from him in person, honestly, I don't remember.


I can't speak for Zach, but in 2019 on The REPL podcast #23 (https://www.therepl.net/episodes/23/), at 00:41:01 and 00:45:31, he talks about being a bit unhappy with how the core team communicated about his arity-optimized vec/hash class proposal.

He then talks about Aphyr's and Chas Emerick's similar experiences, and laments how in the earliest days, it was still possible to contribute, and how when core development closed off, it was never articulated up front until "Open Source Is Not About You", which is its own can of worms.

Overall, it's a good and nuanced discussion, but it's obvious he wasn't in unreserved love with the language, so I'm not surprised he left.


> it's a good and nuanced discussion

Yeah, no, I'm not saying Zach wasn't unhappy with the whole core team vs Emerick drama, none of us were. I kinda get it, but also, I don't get it like at all. There are so many tiny details I don't know anything about, but I also feel it was somewhat personal, not really socio-technological (so to speak). Otherwise, we would've maybe seen a big split and a Clojure fork. I see the similar tension (for years) now in Emacs and Org-mode camps. Contributing to core Emacs has similar pain points - specific workflows (no PRs - patches discussed in the mailing list, tone of dismissal toward community-submitted fixes, etc.), but I don't see any big contributors going like: "fuck it, I'm tired of this BS, I'm gonna go contribute to Neovim instead..."

It is really sad that Chas decided to leave slamming the door, makes it almost impossible to imagine him returning and contributing to let's say to Jank, which afaik has none of the hurdles he complained about.


"inspired by clojure" - is there a better signal for good taste and quality?


Inspired by PHP :^)

Just kidding, even though I both love Clojure & PHP.


"inspired by Scheme"

"inspired by CL"


"inspired clojure" ?


no, thanks


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

Search: