Hacker News new | past | comments | ask | show | jobs | submit login
ClojureScript 1.10.844 (clojurescript.org)
178 points by tosh 6 months ago | hide | past | favorite | 72 comments



This is great!

What is not so great is that Google's closure team (not be confused with clojure) decided to remove various functions, making breaking changes - for no obvious reasons other than saving a few lines of code.

Example Things like `goog.isArray` is gone, a library that was adopted by many (including the CLJS compiler) to help smooth out the differences in JS versions and supported features.

The closure team even has great advice for you:

"goog.isArray has been deleted. Use Array.isArray instead."

Well, how about you just delegate to that, Google team, and I can just upgrade?

This kind of library maintenance is what keeps me away from JS/TS where prior investments seem to be less valuable:

React deprecating lifecycle methods, Typescript's breaking changes page is pretty long (but admittedly, being MSFT, they try to care about backward compatibility), Vue.js 3: functional components deprecated, manual changes required.

Who can afford these shenanigans?

Changes in re-frame, reagent etc. have been a lot more careful. Not quite perfect (looking at you Reagent 1.0!), but I'm sleeping much better working with a stable, slow-moving framework - at the cost of maybe a few more repaints.


https://killedbygoogle.com/

Cljs never should have been built on top of closure.


These are great points. Google's Closure is open source with an Apache 2.0 license [0]. This is a huge ask but do you think a fork of Google's Closure with the goal of minimizing breaking changes be embraced by everyone who currently rely on Google's Closure?

[0] https://github.com/google/closure-library/blob/master/LICENS...


Maintaining backwards compatibility is not trivial as your comment seems to allude to. There's obviously a reason things are deprecated and removed from code bases.

There's also a reason SemVer exists, to notify you to the fact of breaking changes (whether the library maintainers actually abide by the correct usage is another conversation).

Don't want breaking changes? Dont upgrade your stuff, sounds easy to me.


Yeah, never upgrade, fork and get the security updates somehow in that new fork. Not very convenient.

Semver is not an excuse to break your clients.

The reason given was "code size and keeping the API surface small". Both are bogus, especially since this is part of an optimizing compiler who will REMOVE unused code. Leaving simple forward from goog.isArray to Array.isArray in there does not cost any maintenance, does not mean extra tests (you already have those) and does not mean your API surface is overblown.


Since this thread/Clojurescript is getting some love I'd like to express something to a hopefully receptive audience:

There was a point where Clojurescript and Javascript sort of walked hand-in-hand during the advent of when React started to make sense in the minds of the community[0].

It felt really exciting because of the cross pollination that was happening between the two languages, Redux felt very familiar, and we have re-frame for clojurescript.

There was a ton of promise with Apollo.js and an improvement on the `connect` functionality by upgrading to a proper query language for getting state into a component versus wrapping functions around multiple `state.some.path` `state.some.other.path`.

David Nolen built om.next around that very idea, it was basically Apollo.js lite, and it was very exciting scientifically but never quite caught on because maybe it was a little clunky. But using datomic pull query syntax to fetch state for use in a component was thrilling. (if you got this far and decided you hate me and your eyes are rolling so hard I get it lol)

And this was all years ago now. All that excitement and progress and effort seems to have been trapped inside of Typescript, and React hooks, and did everyone forget?

0: https://swannodette.github.io/2013/12/17/the-future-of-javas...


I never did try om.next, but to this day Reagent is my favorite way to interact with React. I too was hopeful ClojureScript would become more mainstream. I haven't coded in Clojure in years now because unfortunately I just don't see any companies I did or would work for ever adopting it.


My impression is that CLJS, while an amazing way to write frontends with react, suffered from an incomplete/fractured tooling ecosystem, with the official maintainers leaning on the community to develop the 'batteries' and several good but incomplete and unofficial solutions being offered (lein-cljsbuild, figwheel, boot, etc).

I'm not actively involved with CLJS development anymore but last I checked that still continued, with tools like figwheel/deps.edn/shadow-cljs all sort of doing similar things differently, and there being good-but-not-as-good-as-clojure support in different editors like emacs, intellij and vscode. Similarly, there were a number of different, but still alpha quality ways, to use cljs with react native, all suffering from some major pain points.


I shared a similar opinion a while ago, but lately (last two years perhaps?) the general consensus is pretty much shadow-cljs which has stellar support for node modules. shadow-expo also integrates with RN effortlessly.

Cursive is really good at handling cljs too, with jump-to-definition and refactoring without using a REPL


Do you know if shadow-cljs support source maps for React Native? (Their absence was a really significant pain point when I worked on a production CLJS/RN app.)


You need two source maps: one produced by ClojureScript and one produced by RN Metro. When you get an error with a stack trace from RN, you need to walk the source maps in the correct order to find where the error in the CLJS code originated. I wrote some custom code to do this but I haven’t polished it/made it into a lib at this point.


Sure, I understand. But that's several orders of magnitude less useful than what one gets in eg the chrome devtools console from a CLJS webapp.

I found this to be a major timesink when debugging things, and never came up with a good bulletproof solution to automate this traversal. It was especially painful with random breakages due to NPM's automatic dependency/transitive dependency upgrades due to version ranges (ie, things that don't just fix themselves by checking out a previous known good version).


All of the most popular Clojure DE's, namely VSCode/Calva, Idea/Cursive and Emacs/CIDER has great support for clojurescript for now.


Can you set a breakpoint in CLJS code within your editor? Can you instrument a CLJS function, call it and do step-by-step debugging like in CIDER or Cursive for clojure?


Fulcro is getting a lot of love lately and a passionate community of users around it. So no, it's not forgotten, just migrated

https://book.fulcrologic.com


Just to add some context for the OP, Fulcro is kind of an evolution on om.next.


AFAIK, albeit it was initially based on the ideas implemented in Om.Next, modern versions of Fulcro are very different today.

I think Om.Next was just a collection of a few, rather conceptual, abstract objectives, quite experimental (perhaps even ahead of their time) and untested in production.

Too bad that Cognitect couldn't give David Nolen a carte-blanche and let him work on it 100% of his work time.

I'd rather live in a world where libraries like Om.Next become popular, and not stuff like Angular.

ps.: I used Angular. And I loved it. Angular made sense. Sometime around 2009-2010. But it stopped making sense. And I stopped liking it. You still can build awesome things with it, and people still do. And I still don't like it.


I don't have any specific knowledge of the community dynamics you're talking about, but I have opinions about these technologies, and can speculate:

Personally I find it harder and harder these days to justify writing code that doesn't have static types. Even for personal projects, if I'm writing more than 100 lines, I want editor checks and standardized documentation. Particularly when I'm not doing anything wildly novel, and particularly (but not exclusively) for larger projects.

It's not hard for me to imagine industry reaching a similar conclusion, especially when the novelty of most of the web apps being cranked out tends to be lower, and the scale tends to be larger.

And where industry goes, hobbyists tend to go so that they get to put relevant stuff on their resume (not to mention paid employees who release in-house tooling and libraries as open source).


this rings pretty true to me. I have a whole host of reasons for wishing I could switch our group into the Clojure ecosystem, but a huge thing holding me back is not feeling confident that I understand how spec 2 would help us efficiently solve the documentation and pre-runtime correctness verification problems that static typing (Flow and typescript and mypy) give us.


Spec isn't directly for static analysis

Spec is for specification which can be used for more things than precompile problem checking

Clj-kondo is for precompile static analysis that also supports minimal typing


Also: cross pollination is still happening as libraries like Recoil implement some of the ideas sitting in Reframe for vanilla React.

On the ClojureScript side, I would like to see more projects integrating with platforms like Nextjs and Gatsby.


There is nothing that says ClojureScript can’t be used on the server side and integrate with the platforms you mentioned. It’s likely just a matter of writing wrapper libraries similar to what was done for many Java libraries.

The hard part is that most Clojure devs default to using the JVM server side so there’s not much appetite there to do the work to embrace JS server side frameworks.


It's actually a bit more convoluted because of how these frameworks compile JS into various artefacts https://clojureverse.org/t/creating-websites-with-shadow-clj...

But you're right, for most "serious" projects, people tend to reach for the JVM. Yet, I think a strong ClojureScript presence on the JAMstack would benefit beginners a lot.


I remember the original Om already being really exciting.


A bit of an obvious question Elm, Typescript and Purescript all offer Javascript + Types

Clojurescript is dynamic, beside the lisp syntax which is arguably cleaner, and offer some nice advantages like homoiconicity

What is the real advantage of Clojurescript (?)


The question is funny because on the the list of advantages that Clojurescript offers the syntax / language is quite low on the list imo.

Clojurescript has the best client-side build tool I have used in https://shadow-cljs.github.io/docs/UsersGuide.html and everyone uses it.

Clojurescript targets/outputs Google Closure compatible code which is easily the most sophisticated JS compiler out there. The reason no one uses it is because writing compatible code by hand is a nightmare.

Clojurescript's front end libraries are incredible, they are basically a better expression of Redux because the Clojure language has constructs that enable a more expressive API https://day8.github.io/re-frame/re-frame/

Clojurescript's JS interop is actually insane, boosted by the quality of Shadow-cljs. You can write 1:1 javascript in clojurescript if you need to. A trite example would be npm installing lodash and writing some code that uses it, I have done this and it just works.

Clojurescript's developer experience with the browser repl is next level. I know a lot of the stuff I'm saying sounds hyperbolic, but I've written a ton of Javascript and react and I really like it still, but using a browser repl to manipulate application state in my editor and reload functions without a full refresh is great.

And I guess lastly, yea the language is awesome. It's basically my perfect version of Javascript. No need to fumble with libraries like immutable.js, no need for additional libraries like ramda or lodash because the standard clojurescript library has everything+.


“It's basically my perfect version of Javascript. No need to fumble with libraries like immutable.js, no need for additional libraries like ramda or lodash because the standard clojurescript library has everything+.”

This pretty much my view as well. There’s that classic joke comparing two books on JavaScript, the complete language vs. the good parts [0], and you could pretty much just replace the “good parts” book with ClojureScript.

It has everything you need and gets rid of most of the stuff that enables people to write bad code. (Assuming you like functional programming, of course)

[0] https://i.redd.it/h7nt4keyd7oy.jpg


Sorry for what might be an obvious answer, but when you say

>because the standard clojurescript library has everything+

do you mean bc it has all those libraries incorporated, or bc it's able to emit the equivalent to native javascript?


Immutability is baked into ClojureScript, so there’s no need for libraries like Immutable.js. Likewise, ClojureScript is all about manipulating a small set of code data structures (lists, vectors, maps, etc), and as such it has an extensive (!) set of functions built into the core language for doing just that, which eliminates the need for libraries like Ramada and Lodash.

Behind the scenes ClojureScript is transpiled to pure JavaScript with Google’s Closure compiler, so there’s no external dependencies on libraries like the ones mentioned above. It’s all just built into the language.


Reading this and other replies I wonder why no one mentions core.async?


core.async is great, but admittedly I haven't needed to reach for it on the client (when building SPAs). I'm not sure if it's because re-frame is the focus for event driven workflows or what, but...


One of the underated aspects is the inbuilt Google closure library https://developers.google.com/closure/library

So nice to have a good standard library when using JavaScript

My favourite "type checker" is clj-kondo


Yea it's funny how much is in there, between it and the Clojure standard library you basically don't need to go searching for third party libraries.

I needed a debounce the other which was just importing

`[goog.functions :refer [debounce]]`


For me, the primary advantage has been the ecosystem.

Reagent and re-frame have been awesome since Day 1, even as react has tried to play catch-up with hooks etc.


Very interesting take from re-frame's author regarding React Hooks (and why it ultimately doesn't matter that hook-based components are not very popular on CLJS): https://github.com/day8/re-frame/issues/590#issuecomment-624...


I’ve been writing a lot of react over the past several years (JS/TS), and I strongly agree with this take: hooks, especially hooks like useState really are an attractive nuisance that, by making it easy to have local state in components, end up spreading business logic all over the view layer and coupling all the wrong things together.


Agreed.

I would not use local state apart from fine-grained performance optimizations or temporary, always-can-be-thrown-away state specific to the component itself.

Example: email field input before passing validation.


I’ve had the experience, though, that “temporary” state often becomes a feature requirement. Like, you have a field to search your dataset by email address and, next iteration, the product manager wants autocomplete based on partial input.


The autocomplete results can come from the global app state. There’s no rules written in stone about what should be in local state. My default is “as little as possible”.


My point is that I’ve found that there is very little local state that doesn’t eventually benefit from being merged into the global state management store.


Fair enough.


For me the real advantage is the shared information model with the backend that is trivial to serialize/de-serialize with something like transit-clj(s). It's like the benefit of js/node but with richer data structures: keywords, symbols, sets, maps with arbitrary keys, instants, uuids and extensibility via tagging. JSON is great, but EDN is so much better!


It offers REPL, great hot reloading, a small set of immutable data structures, and a great set of carefully architectured core library functions to work with those data structures.

Also because of immutable data structures it has a very clean comparison semantics e.g. you can compare data structures by value with `=` which helps a lot in React-like environments.

The absence of static typing enables you to write more general code with less effort. All that leads to simple, clean and compact code. If you check Real World App statistics [1] you'll see that Clojurescript/Re-frame implementation has the fewest LOC (almost 4x less than Elm and 3x less than Purescript).

[1] https://www.freecodecamp.org/news/a-realworld-comparison-of-...


Can share much of the same code on front end / backend. Excellent built-in immutable data structures. Arguably the best modern lisp. Community is usually ahead of its time on state management solutions.

Typescript is probably a better fit for most people doing most things.


to name a few: immutable data structures out of the box (with literal syntax), macros, being able to share code with the back end (cljc).


> What is the real advantage of Clojurescript

IMO. Interactive development. It allows you to connect to a running program (local or remote) and tweak things on the fly. Without even affecting the state of the program. Without even having to save or re-compile. It's hard to explain the benefits of that approach. One has to experience it.

Another major benefit: true code re-use. You can write functions that would work both in Clojure and Clojurescript (even though we're talking about very different environments). With Javascript/Typescript (in practice) that is hard to achieve even when you have a nodejs backend.


You forget Scala with Scala.js, which is fantastic.

https://www.scala-js.org/


The benefits of using Clojurescript over JS/TS in 2021 are superficial and not worth it IMO. Typescript is great, has good tooling and plays nice with the JS ecosystem. With Clojurescript you spent more time fighting integration with the JS ecosystem than getting any real work done. The mismatch between JS objects and Clojurescript data structures is annoying and tedious and riddle with performance issues, and Clojurescript in general has many performance issues.


Choice of abstractions which suit your thinking is obviously subjective so some people naturally prefer TS or Scala.js or PureScript over Clojurescript, (or whatever else over Clojurescript). However, the statement "you spent more time fighting integration with the JS ecosystem" is evidently not true. In my own experience you can do almost everything never leaving CJS realm, and when you need to combine it with existing JS code it will require some deeper understanding, but nevertheless goes pretty easy. Didn't downvote you btw


I usually avoid a direct ad-hominem attack but here I think I have to.

Most things this person says in every thread about Clojure are false.


That account belongs to a regular Clojure-hater, it's seems the person is biassed against it at the personal level.


Sounds about right.


Could you give us more details about the problems you running in?


For those with experience, would you say that ClojureScript for frontend stands on its own without a Clojure backend?


I’m a bit biased, but from my personal experience I would definitely say that it does. I’ve worked on two projects that had different backends (PHP and elixir) where the choice to use clojurescript IMO was the correct one. Both projects relied heavily on highly interactive frontends that would’ve been a nightmare to write if I had to use vanilla js. If your backend can spit out json, and your frontend isn’t static, then I’d say cljs should be good to use regardless if the backend is clojure or not. However, the biggest pain point for me was the disappointment of not being able to use clojure everywhere in the stack... so prepare for that if it’s the route you choose.


It depends on what kind of project you are using it for.

Hobbyist/toy project - sure, using shadow-cljs you can do your front end entirely in ClojureScript and make calls out to whatever backend you want

Professional project with ClojureScript backend - sure, you can use shadow-cljs to target both node (using :node target) and the browser which will allow you to utilize ClojureScript for full stack and not have to rely on Clojure

Professional Project with non-clojure(script) backend - no. It would not be worth it to spend resources to learn clojurescript (talent pool of existing clojure devs is small) and not taking advantage of full stack clojure(script) can be detrimental


> Professional Project with non-clojure(script) backend - no.

Allow me to offer a contradicting point of view. On my last project we used Elixir on the backend and Clojurescript (with re-frame) on the frontend.

Best decision we've ever took. We were orders of magnitude more productive than we would've been with Javascript. And we were all beginners to Clojure at the time.

Re-frame is a beast. Not having to deal with Javascript and its ecosystem is an absolute relief. Once you use Hiccup for the first time, you'll never want to use JSX ever again.

So yeah, if it's a new project in my opinion it is worth exploring Clojurescript, even if you don't plan on using Clojure.


Same experience here except with python as the backend.

We could not have done half the things we did without ClojureScript on the front end.

Also, the learning curve is not very steep if you're just doing a re-frame front ends. You really just need the basic 1/3 of Clojure, and rarely if ever touch all the complex parts.


> Best decision we've ever took. We were orders of magnitude more productive than we would've been with Javascript.

Would probably have been the same boost in productivity if you had chosen TypeScript though. And choosing TypeScript obviously comes with numerous benefits (large pool of developers, ecosystem, first-class integration in VSCode, backed and highly maintained by Microsoft, etc).


> Would probably have been the same boost in productivity if you had chosen TypeScript though

Nope. It wouldn't. You can replace "Typescript" in your sentence with pretty much any other "-script" thing: Coffescript, ScalaJS, KotlinJS, etc., and it still wouldn't be closer to the truth.

The biggest advantage of Clojurescript is out-of-the-box support for interactive development. You are connected to your running application. And without any ceremony, without re-compiling or even saving anything on the disk, you can tweak the program without resetting the state of it. If you have never experienced that first hand, it would feel like it's not a big deal. Alas, it really is.

Clojurescript right now (at least for me personally) is the fastest way to prototype anything for the browser. Building things like web-scrapers feels like magic. You just run the program, connect to it and keep experimenting with things until you get the results you want. No other language ecosystem today gives you that liberating feeling. Almost everything else feels too formal and bureaucratic - can't do "this" without "that". It becomes pretty tedious and often removes "the fun" from programming.

So, yes, I attest to that - Clojurescript can give you incredible productivity boost.


It seems fairly subjective. I'm familiar with Clojure and happily use it at work, but I haven't seen anything in CLJS that comes close to the power of automatically generating types for GraphQL queries, for example. With a single command, I get notified if some client queries do not respect the server schema, plus separate types for each response which provides autocomplete and information about each field (type, nullability...) right in the editor. I don't feel the need to reload a bunch of times because everything usually works on the first try. I still need to tweak the CSS but I tend to prototype that in the browser dev tools anyway.

I'd pick CLJS over JS but not over TS.

On the backend, I'd pick Clojure over both JS and TS in a heartbeat, though.


I admit I have not used GraphQL in Clojurescript. However, Clojure has its own type-system. Of course, purists would argue that Clojure.spec cannot be categorized as a type system, albeit it is, and you can do pretty cool things with it.

For example, you can build a suite of specs that denote a ledger - where amounts in every transaction depend on each other. That is not a trivial task - most other type-systems don't give you good instruments to achieve something like that. You can then use those specs to generate data for property-based tests.

You can also use them to validate the data flow in the back-end for different data stores. And re-use the same specs on the front-end for input validation. Clojure and Clojurescript allow you to get as much code-reuse as possible (even though the code has to run in completely different environments).

> I don't feel the need to reload a bunch of times because everything usually works on the first try.

Perhaps you haven't had to build reasonably complex front-end applications, where a user has to go through many steps in the UI. With a lot of state and state transitions involved. I spent several years building web-front ends. Used if not all, but most popular ways to compile/transpile/deal with Javascript. I'd choose Clojurescript, because it makes sense for me today.

Tomorrow perhaps something better comes up. But Typescript today is not an option for me. I have tried it multiple times in multiple projects. "Joy of Typescript" doesn't even sound right. "Joy of Clojure" though is a thing. People love Clojure and Clojurescript, and not without good reasons.


I currently use Typescript at my day job, and before that I used Elm.

Based on my experience, no, it's definitely not the same boost. Elm felt more productive than Typescript, and Clojurescript felt more productive than Elm.

And yes, when compared to Javascript, Typescript is awesome and well worth the investment.


Absolutely, shadow-cljs is all you need, it's not coupled to the Clojure ecosystem, it uses NPM. I'm actually working on a project that uses clojurscript on the backend which is just a `:node` build target in Shadow.


Same here. I’m slowly writing some wrapper libraries for server side ClojureScript (starting with Express.js).

It’s a fun side project for now, but hopefully I can use it to convince some of my JavaScript loving co-workers to give Clojure a try.


I'd say so. I used it for a relatively large front-end (leveraging Reagent) with the backend served by Django Rest Framework. Worked beautifully!


Definitely so. We use Clojurescript with Python backend and it's great. Clojurescript is very good at json processing/manipulating, so it does not matter what backend you have.


On a related vein, is cljs viable for PWAs and purely static front end only apps?


I would say so, I have implemented a pwa https://djblue.github.io/portal/ hosted via github pages for people to browse through their data-structures. You can also connect the ui to a live host and send it data directly with tap> (kinda like console.log in js).


Any updates on Lumo? That was a great workflow.


If I remember it right, Lumo and Planck (mainly) were created to deal with the long startup times of Clojure. People needed a faster way to get the REPL up and also something that can run tiny scripts. With the emergence of Graal, and tools like babashka, it looks like the JVM warmup time is no longer that big of a problem.


I just saw the original developer is now focusing on a fork of ocaml's bucklescript so I'm not sure how much more work it's going to have.




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

Search: