17 comments

[ 3.7 ms ] story [ 40.0 ms ] thread
I feel similarly about ClojureScript. It's a neat proof-of-concept, but I'm not really sure what it adds over regular JavaScript (or at least Babel + Webpack) these days.
My team finds that it adds a lot. For starters, it's a much cleaner and simpler language. This means that you have a lot less mental overhead working with it.

It encourages good practices by defaulting to immutability. This naturally leads to code that can be reasoned about in isolation.

I find the tooling is superior. Leiningen lets me do the same thing as a grab bag of Js build tools, such as dependency management, building, packaging, and so on.

There's still no equivalent to Figwheel in JavaScript workflow. The productivity of being able to see changes as you're working without having to reload the page and rebuild state can't be overstated.

Reagent/re-frame combination much cleaner than React itself in my opinion. It also avoids much of the complexity in React as described here https://purelyfunctional.tv/article/react-vs-re-frame/

> For starters, it's a much cleaner and simpler language. This means that you have a lot less mental overhead working with it.

I haven't seen much code that uses the bad aspects of JS, or at least exposes it in library APIs. You can just stick to the good parts of JS and have the same benefit. Clojure also has its quirks, btw. Every language will.

> It encourages good practices by defaulting to immutability. This naturally leads to code that can be reasoned about in isolation.

The language may encourage it, but given that none of the JS libs I've used mutate anything I give them, it sounds like the benefits of immutability have spread enough that mutation is no longer a problem in practice.

> I find the tooling is superior. Leiningen lets me do the same thing as a grab bag of Js build tools, such as dependency management, building, packaging, and so on.

If you write a good set of config files with something like Webpack, it's fire-and-forget. And in practice, Leiningen isn't all that simpler than JS alternatives. Trying to understand the sample project.clj for a real-world ClojureScript project was kind of a nightmare for me.

> There's still no equivalent to Figwheel in JavaScript workflow. The productivity of being able to see changes as you're working without having to reload the page and rebuild state can't be overstated.

We'll have to agree to disagree. I can make changes to my site and the page reloads live with only 14 lines of vanilla JS code on the front-end[1] and back-end[2] each. If I need to make changes while keeping state instead of reloading (which I haven't really seen a need for) I'm sure I can whip something up using `eval`. Plus even if you follow the Components architecture and avoid global state, there are still situations where you have to reload the page from scratch.

> Reagent/re-frame combination much cleaner than React itself in my opinion. It also avoids much of the complexity in React as described here https://purelyfunctional.tv/article/react-vs-re-frame/

That may be true, but that doesn't prevent equivalent libraries to Reagent/re-frame from being created in JS. And maybe there already is an exact copy of that in JS somewhere. React may be the king in this space for now, but there are lots of alternatives or plugins while sticking to JS.

[1]: https://github.com/sdegutis/sdegutis.com/blob/master/src/tem...

[2]: https://github.com/sdegutis/sdegutis.com/blob/master/index.j...

>I haven't seen much code that uses the bad aspects of JS, or at least exposes it in library APIs. You can just stick to the good parts of JS and have the same benefit. Clojure also has its quirks, btw. Every language will.

Every language will have quirks, but Js certainly has a lot more quirks than Clojure. My team saw a huge gain in productivity moving from Js to ClojureScript on the front-end.

>The language may encourage it, but given that none of the JS libs I've used mutate anything I give them, it sounds like the benefits of immutability have spread enough that mutation is no longer a problem in practice.

That's certainly not my experience. Everything in Js is passed around by reference, and it takes a lot of discipline for teams to write applications in a maintainable way.

>If you write a good set of config files with something like Webpack, it's fire-and-forget. And in practice, Leiningen isn't all that simpler than JS alternatives. Trying to understand the sample project.clj for a real-world ClojureScript project was kind of a nightmare for me.

Leiningen is also fire and forget when you use a template. I don't think understanding Webpack and all of its config is any easier.

>We'll have to agree to disagree. I can make changes to my site and the page reloads live with only 14 lines of vanilla JS code on the front-end[1] and back-end[2] each.

This works fine for small applications, however in large real world apps where you have a lot of state reloading a page every time you make a change is just a non-starter. I might need to login, navigate to a specific section, load some data, and then work on the presentation. Repeating all these steps each time I change a few lines of code is absolutely insane.

>That may be true, but that doesn't prevent equivalent libraries to Reagent/re-frame from being created in JS.

Actually it does. Js syntax sucks for writing HTML, and that's why you have stuff like JSX in the first place. Now you have an additional DSL with all of its quirks on top of already quirky Js.

> Every language will have quirks, but Js certainly has a lot more quirks than Clojure. My team saw a huge gain in productivity moving from Js to ClojureScript on the front-end.

Were they using ESNext features? Where they using Babel to compile to ES3 or ES5? Or were they just using the old-fashioned JS that's filled with tons of huge warts? Because for me, the newer JS features are definitely a game changer. Not only do they reduce a ton of boilerplate code for me, but they make things safer, more predictable, easier to reason about, easier to catch bugs, and generally prettier.

> That's certainly not my experience. Everything in Js is passed around by reference, and it takes a lot of discipline for teams to write applications in a maintainable way.

There are several built-in methods in modern JS for returning new objects based on existing arrays and objects such as map, filter, and reduce, and there are libraries to polyfill basically the rest of `clojure.core` into it such as lo-dash.

> This works fine for small applications, however in large real world apps where you have a lot of state reloading a page every time you make a change is just a non-starter. I might need to login, navigate to a specific section, load some data, and then work on the presentation. Repeating all these steps each time I change a few lines of code is absolutely insane.

Unless your site needs to be an SPA, most of this is a non-issue. Just keep login information in either a cookie or localStorage/sessionStorage, and live-reload the page without worrying about state.

> Actually it does. Js syntax sucks for writing HTML, and that's why you have stuff like JSX in the first place. Now you have an additional DSL with all of its quirks on top of already quirky Js.

Sure, there are situations where you can't have your code be as nice without using some kind of compiler phase. ClojureScript macros may come in handy with this, but that still has its own caveats to be avoided. In my experience, it's not much different than the overhead you get from using JSX itself or a similar compiles-to-JS template language.

>Were they using ESNext features? Where they using Babel to compile to ES3 or ES5? Or were they just using the old-fashioned JS that's filled with tons of huge warts?

Each extension of Js adds complexity to the overall language with its own quirks and warts. Different libraries will be written in different styles and using different features, and you have to juggle all of that in your head to work with the language effectively. I think we have plenty of evidence nowadays that working with large languages introduces a significant mental overhead.

I don't see why I'd want to work with a large quirky language when I have a simple and focused one available to me. With all the new features that ES6/7 add, Clojure still ends up being more concise vast majority of the time.

>There are several built-in methods in modern JS for returning new objects based on existing arrays and objects such as map, filter, and reduce, and there are libraries to polyfill basically the rest of `clojure.core` into it such as lo-dash.

Or I can just use a language that was designed around immutability and avoid all that. What is the advantage here?

>ClojureScript macros may come in handy with this, but that still has its own caveats to be avoided. In my experience, it's not much different than the overhead you get from using JSX itself or a similar compiles-to-JS template language.

You don't need any macros for this. ClojureScript syntax maps really well to HTML because it's s-expressions. Reading hiccup syntax is actually easier than reading plain HTML in my experience. Also, since it's just regular data, it's trivial to manipulate it in the language. There are no new rules to learn, no DSLs to memorize, it's just plain data.

> I haven't seen much code that uses the bad aspects of JS

> The language may encourage it, but given that none of the JS libs I've used mutate anything I give them

> If I need to make changes while keeping state instead of reloading (which I haven't really seen a need for)

Speaking as an enterprise JS dev the kind of apps you are working on sound nothing like the kind of apps I work on.

I work with a lot of poorly written JS code, a lot of mutable state, and pages with lots of complex state setup.

In fact the state setup is typically so bad developers rely upon unit tests and only check the UI on feature complete.

Sorry to hear that. And that kind of code damage sounds pretty typical from most projects I've seen in open source and heard about through people I've talked to, no matter what language these projects use. It must just be that we all learn the hard way and usually during work hours what works and what doesn't. In cases like this, Clojure can help somewhat by its saner defaults, but I've seen some really terrible Clojure code too, that has many of the same problems.
ClojureScript forces the whole ecosystem into doing immutability, properly, all the same way
Have you worked with Leiningen? Try it and compare it to the hideous monstrosity that is Webpack. Have you worked with Figwheel? Try it and compare to debugging console.log statements.
Your parent addresses both elsewhere in this thread, for example:

> [I]n practice, Leiningen isn't all that simpler than JS alternatives. Trying to understand the sample project.clj for a real-world ClojureScript project was kind of a nightmare for me.

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

>These days I prefer writing in modern JavaScript (ES2015 and better). With destructuring, lambda syntax, extended object literals, spread syntax, and just the general language overhaul, it's basically a tie with Clojure's terse syntax.

One of the biggest advantages I find with Clojure syntax is that there's very little of it, and it's much more consistent than JavaScript. Sure, you can often write code that's just as concise in Js nowadays, but there are a lot more gotchas as well. The more syntax quirks you have, the more likely it is that you'll misread something and have a subtle bug slip in. I value simplicity and cleanliness of the language as much as its expressiveness.

The article also doesn't explain why the author moved to JavaScript. It sounds like the author didn't want to use the JVM, but ClojureScript works just fine on Node. My team is using Macchiato https://macchiato-framework.github.io/ for some smaller projects in production right now.

I'd be interested in reading more about the specific reasons, and what problems the author ran into with the Clojure stack. That would at least help address the issues going forward.

Author here. My main reason for moving away from Clojure and ClojureScript, and not towards Haskell or OCaml or even TypeScript (yet!) but rather towards modern JavaScript, is because in general I've been learning to appreciate the Path of Least Resistance in software development. I wrote another aspect of this in Age of the Polyglot[1], which focuses on the friction when bridging loosely-compatible tools, but there's other friction points to the non-mainstream ecosystems, like having a stagnant or decaying community, lack of libraries, etc. A lot of active development seems to be happening in the Node world in the past 5 years, and although there's probably a ton of crap, there's also a ton of gold, and that's kind of what happens when you have a thriving community.

[1]: https://sdegutis.com/blog/2014-08-18-age-of-the-polyglot/

My team has been using Clojure for about 7 years now, and we started using ClojureScript about 2 years ago. We're certainly finding it to be the path of least resistance compared to keeping up with Js. For example, we've been using Reagent for the front-end, and it's stayed the same all this time. Even as React itself keeps changing, all we have to do is update the library version, and everything just keeps working.

Keeping up with the way Js world evolves is absolutely bonkers in my opinion. By the time you finish writing an app, the whole stack becomes outdates as people jump onto new hot thing.

Even if you stick with something like React, the practices around it keep shifting, you now have Redux and Relay, and god knows what else people will come up with next week. Having a stable development environment is a big benefit in my book.

I'm also not seeing any problems with Clojure community being stagnant or decaying. It keeps growing steadily, and the library ecosystem is quite excellent at this point. Since Clojure embraces the host platform, you always have access to native libraries as well. If a cool library gets developed in Js or Java, it's immediately available in Clojure.

Node has a lot of hype, but I haven't seen anything developed on Node that's not available on the JVM. The only advantage of Node I see is that it takes less resources. Having actually developed some ClojureScript apps on top of Node, I think that it's a strictly inferior experience. Callbacks are much harder to work with than threads, and even promises and async result in much gnarlier code. Given my experience with it, I would only use Node for very simple applications.

> Keeping up with the way Js world evolves is absolutely bonkers in my opinion. By the time you finish writing an app, the whole stack becomes outdates as people jump onto new hot thing.

I definitely agree which is why I haven't sunk a whole lot of time into learning any of these in particular just yet. I've stuck with vanilla JS for most front-end needs and Vue.js when I need something slightly more complex, with no build phase.

> Since Clojure embraces the host platform, you always have access to native libraries as well. If a cool library gets developed in Js or Java, it's immediately available in Clojure.

True but I don't see a whole lot on that front in the JVM world either. There's a few libraries that pop up here and there, but most Java work I've ever seen is done using existing tools and frameworks, unfortunately.

> Node has a lot of hype, but I haven't seen anything developed on Node that's not available on the JVM.

I look at it the other way around: In an incredibly short time span, NPM has pretty much covered every library that the JVM has.

> The only advantage of Node I see is that it takes less resources. Having actually developed some ClojureScript apps on top of Node, I think that it's a strictly inferior experience.

From a technical perspective, Node is probably inferior to JVM in most ways, yeah. But not in ways that matter significantly. Lately I've been considering social aspects to be a lot more important than technical aspects, though. I mean, Rails got popular while sitting on top of an AST-traversing Ruby interpreter. At the end of the day, as long as it's semi-usable, hype and an active community are probably more important than all the other facets of a language, at least for most web apps.

>true but I don't see a whole lot on that front in the JVM world either. There's a few libraries that pop up here and there, but most Java work I've ever seen is done using existing tools and frameworks, unfortunately.

I still don't understand what the problem here is. Can you explain a concrete business problem you were trying to solve and found Clojure inadequate for. What was the scenario where you couldn't find a library for a specific task, or couldn't solve a problem because the language was deficient.

>I look at it the other way around: In an incredibly short time span, NPM has pretty much covered every library that the JVM has.

Threads is a great example of something that Java has and makes a huge difference in the developer experience. Having to chain async calls for every single thing you do is absolutely insane in my experience. Following code becomes a lot harder, debugging is a nightmare, and passing errors around has to be done explicitly in error prone fashion.

>From a technical perspective, Node is probably inferior to JVM in most ways, yeah. But not in ways that matter significantly.

See above.

>Lately I've been considering social aspects to be a lot more important than technical aspects, though.

Sure, let's talk about social aspects. Clojure community is primarily composed of very experienced developers and that directly reflects the quality of tooling and libraries available. Last I checked Clojure community is very active and it's not going anywhere. It's used at places like Amazon, Boeing, NASA, and Walmart for mission critical tasks. I certainly value quality over quantity here.

Tbere's also a reddit.com/r/clojure thread FWIW.