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

By the way, Clojurescript is one of the greatest tools I've ever experienced in the software industry. And it is maintained by a real smart guy that gets paid by Cognitect to work on it. Isn't this a case of Cognitect spending money to improve free tools for the world? Which is the opposite of the original article's accusations?


Maybe it has gotten better in the last year or so, but I’ve been extremely disappointed in a) the scope of semantics divergence between Clojure and clojurescript, and b) the performance of clojurescript for anything larger than toy examples. After reading a blog post about scalajs[0], I came away pretty convinced that battle would be uphill. It’s just too hard to replicate one dynamic language’s semantics in another dynamic language without embedding huge and slow runtimes along with it.

[0] http://www.lihaoyi.com/post/FromfirstprinciplesWhyIbetonScal...


I don't know what problems you were having, but the performance has been extraordinary for the enterprise app that I work on (now 3 years old). In fact, the product would not even exist without the tools clojurescript brought to the table.

I don't know what you mean when you say the semantics are different between the two versions of the language, they really have the same overall design and I feel like I'm working in the same language when I switch between them.


Looks like you forgot to link to the Scala.js blog post. FWIW, as a Scala.js user the combination of Bucklescript + Reason ML looks pretty compelling: static types and lightning fast build times.

Haven't used Clojurescript but I'd imagine the code-change-reload cycle is quite fast compared to Scala.js, which while absolutely excellent in many ways, suffers from the dog slow compiler that is scalac (Scala collections blowing up generated binary size is the other drawback that comes to mind).


> the code-change-reload cycle is quite fast

I work on a 20K line enterprise CLJS app. Due to incremental compilation where only recent changes need to recompile during development, each code change (meaning, saving some source files) is on average 1 second of recompilation. With the Figwheel tool, there is no manual reload required, as it automatically refreshes the browser to reflect your most recent change.


> each code change (meaning, saving some source files) is on average 1 second of recompilation.

That's actually fairly slow, I'd expect pretty much instantaneous (hot reload is nice though). The overhead of Bucklescript, for example, is at most in the 10s of ms for code changes (saw a 2017 Strange Loop talk where the presenter claimed 200ms to compile the entire project).

Scala.js is around 2 seconds for shallow changes (affecting only one source file), which over time is a productivity drain, those seconds add up when doing front end work.


Always wanted to try bucklescript. Even has built-in persistent data structures, which is a win over purescript.


Sorry abou that, just added it.


Your claims are baseless. If there are any semantic differences between clojure and clojurescript they are so minute that I'm not aware of them. I'm a full stack developer and use both daily. There are no performance issues inherent with using clojurescript either. I say this having made a non trivial UI and pushing loads of data through it and seeing what everyone else has made. Check out precursor app.


There is a significant amount of divergence in semantics. I’m not sure why you would try to convince anyone otherwise...the clojurescript developers acknowledge this completely upfront.

https://clojurescript.org/about/differences

In my experience, all of the stated benefits of clojurescript become more and more tenuous as your intensity of use increases. Hello world seems fantastic, todomvc still seems pretty cool, but by the time you’re doing full size apps of meaningful complexity, you really start to see the limitations. In order to get any meaningful performance out of it, I ended up having to rewrite and experiment like crazy...the code looked like nothing I would have written in Clojure, using completely different idioms and data structures. And while the prototyping was fast, in the end it took me far longer to write and then iteratively optimize than it would have to write an equivalently fast version in scalajs or typescript. Maybe your experience is different, but my experience is my experience, and it most definitely is not baseless.


I've never had to make a context switch between clojure and clojurescript. If differences in semantics means I have to change how I think about solving the problem at hand, then that has been pretty much non existent in my experience. Of course with one you have browser APIs and the other is java, but when it comes to building up and transforming datastructures everything I've been doing works the same on both.

This is also why most the time you can just tack on a "c" on the extension and you have something that works on both clojure and clojurescript.

Performance is not an inherent problem of clojurescript. It was just the way you coded you prototyped your app. Your experience is valid but don't blame the problem on the tool when it was the way you solved your problem before you optimized it.

I absolutely mean no disrespect to you but I don't want your comment to dissuade others from trying out what has essentially been a utopia for me. It has allowed me to build non trivial webapps without having any familiarity with functional programming or lisp. The feedback loop is so damn tight that I was productive without knowing much of anything.

But maybe that's okay. If the everyone else is constantly updating their build tools to every flavor of the month and having to learn new versions of javascript, that gives the few of us who are mastering clojure/script a bit of an advantage.


> I've never had to make a context switch between clojure and clojurescript. If differences in semantics means I have to change how I think about solving the problem at hand, then that has been pretty much non existent in my experience. Of course with one you have browser APIs and the other is java, but when it comes to building up and transforming datastructures everything I've been doing works the same on both.

In clj:

> (+ 1 "1")

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number

In cljs:

> (+ 1 "1")

"11"

Now I get it...not that big of a deal, right? That's what I would say given a trivial example of something like this. Sure, this problem might not come up very often. In fact, for a team that I worked with, it only ever came up once...it's just that that one time that it came up, it corrupted 3 months worth of data. Would tests have caught this? Of course...but if you are confident in your knowledge of the clojure language, and you assume clojurescript is semantically equivalent, you would expect an exception to pop up in your repl during development, so if it works fine in clojure, why write a test for it in clojurescript?

I understand why the cljs designers would do such a thing...think of the performance implications of having to generate type checks code for every possible arithmetic operation! But it absolutely was a context switch for that team (full stack clj/cljs), and the consequences of the developers not correctly context switching was horrific. Nothing quite burns bridges with entire languages like silent errors do.

I'm fine with people trying clojurescript. FWIW, my personal experience with it was never as bad as that one team's experience. Maybe they'll never come across such a dangerous situation, hopefully they don't. But on the rare occasion that some third party web service dependency decides to serialize BigDecimals as Strings instead of Doubles, they deserve to be warned.


To be fair, in your example, the compiler does warn you:

      WARNING: cljs.core/+, all arguments must be numbers, got [number string] instead. at line 1 
Also, Clojure.spec would enforce it.


I have to agree that it is much more likely the code was not written well or idiomatically if there were performance problems. Still, I'd be very surprised that the problems actually were there.

And I actually test my clojurscript lines that I write in a JVM repl! Semantics between the two languages are virtually identical, I can test out my ideas for the JS target in a Java environment. Perhaps the OP has a different definition of "semantics".


I have two questions.

1) You wrote:

> It’s just too hard to replicate one dynamic language’s semantics in another dynamic language without embedding huge and slow runtimes along with it.

I don't understand this. Clojurescript's "runtime" itself most compiles away in Closure advanced optimizations stage (in fact a compiled Hello World Clojurescript app is only 1 line of JS -- no added runtime at all). I don't understand what about the Clojurescript runtime process should be "huge" and "slow." This contrasts with other languages like Elm that do indeed embed a large runtime with the program, but Clojurescript does not do this.

2) I am genuinely curious what performance problems you had. I'm working on a very large Clojurescript app that processes dozens of real-time incoming data points every second over a websocket and performs real-time analytics and graphical display. If ever there was a test for performance, this would be it. Aside from the rare and simple bottleneck that needed some extra thought (maybe 2 or 3 such moments in 3 years of development on the project), there have been nearly no obstacles relating to runtime speed for the app. The 2 or 3 bottlenecks I mention had minor solutions and would have occurred in any language, and were very easy to resolve. And Clojurescript's fast, immutable data structures have inspired major libraries and even other languages entirely. They are battle-tested and performant. So I'd like to know what specific issues you had, as that would be educational.


> I don't understand this. Clojurescript's "runtime" itself most compiles away in Closure advanced optimizations stage (in fact a compiled Hello World Clojurescript app is only 1 line of JS -- no added runtime at all). I don't understand what about the Clojurescript runtime process should be "huge" and "slow." This contrasts with other languages like Elm that do indeed embed a large runtime with the program, but Clojurescript does not do this.

Clojurescript now does a good job with Closure's advanced optimizations, but it has upper limits to how well it can do. For a full sized app, you can expect clojurescript's runtime code to bloat to 100k to 150k...which isn't the end of the world, but it certainly isn't good either.

While clojurescript and javascript are both dynamically typed, their type systems are semantically different. In order to approximate the semantics of the clojure type system, clojurescript has to build its own dynamic type checking system into a system that is already dynamically type checked. FWIR, the reason that clojure.spec was built into the standard distribution of clojurescript (whereas it is an optional library in clojure) was so that the compiler could perform type erasure on code with type hints...something done by default in static languages like bucklescript, scalajs, and typescript.

So while clojurescript does a good job with dead code elimination specifically, thanks to the closure optimizer, it is still a language that is inherently hard for compilers to otherwise optimize.

> 2) I am genuinely curious what performance problems you had. I'm working on a very large Clojurescript app that processes dozens of real-time incoming data points every second over a websocket and performs real-time analytics and graphical display. If ever there was a test for performance, this would be it. Aside from the rare and simple bottleneck that needed some extra thought (maybe 2 or 3 such moments in 3 years of development on the project), there have been nearly no obstacles relating to runtime speed for the app. The 2 or 3 bottlenecks I mention had minor solutions and would have occurred in any language, and were very easy to resolve.

The internet is full of examples of clojure programmers asking how to optimize code up to the already pitiful levels of performance that you can get out of javascript. Every language has it to some degree, but for some reason, it was always a 10x-100x difference for me. Collections in particular were always really bad for me. And the suggestions I received from more experienced clojurists always ended up pushing me away from what I considered to be idiomatic in clojure. Use defrecord instead of hashmaps, use javascript arrays or vectors instead of seqs, use goog.stringbuffer instead of str, use type hints whenever possible, etc.. Fast clojurescript and "idiomatic" (whatever that is supposed to mean in a lisp) clojure were far enough apart that it might as well have been scheme vs clojure.

> And Clojurescript's fast, immutable data structures have inspired major libraries and even other languages entirely. They are battle-tested and performant.

Clojurescript's immutable data structures are indeed pretty fast for immutable data structures run on a javascript runtime. But they are hardly fast. In fact, here[0] is an example of what I was talking about before: cljs written in an idiomatically clj style that performed terribly when used in cljs. And the answer on how to speed it up was to make it more like javascript!

I'm well aware of the rounds of praise that Om got when they showed how using cljs' native immutable data structures sped up React code over that written in javascript. I'd be careful to extrapolate too far with that. That optimization happened to involve a niche benefit of immutable structures (equality checking can use O(1) reference equality instead of O(n) structural equality) addressing a niche bottleneck in React (equality checking in the virtual dom). Overall, immutable datastructures tend to be about the same speed on most operations, but drastically slower on some operations, and drastically more memory hungry to boot. On servers with knowable processing power and knowably large quantities of jvm memory, that's often a tradeoff worth making for the semantic benefits of immutability. On some random user's computer using whatever javascript VM that they likely chose without consideration for how I would use it, I always felt bad making that tradeoff.

[0] https://stackoverflow.com/questions/21721028/how-to-improve-...


Agreed - Clojurescript is a ton of fun on the frontend!




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

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

Search: