https://www.inria.fr/en/jean-marie-hullot-visionary-computer...
12,589 karma · joined January 12, 2009
https://convivivialcomputing.net
https://www.inria.fr/en/jean-marie-hullot-visionary-computer...
So less about ClojureScript specifically, and more generally how I think we're well situated for people looking for a way out. The current mainstream practice dead end is bigger than the one that made React (also originally just a script tag include) appealing to me back in 2013. There are of course many ways forward that don't involve CLJS, but I think ClojureScript/GCL and the new crop of NPM-dep free pure CLJS solutions like Replicant are well situated for folks who can see that accepted practices are not delivering enough value even with AI assistance.
The cost of slop is >40X drop in performance? Pick any metric that you care about for your domain perhaps that's what you're going to lose and is the effort to recover that practical with current vibe-coding strategies?
On OS X you can achieve this with Keyb, Karabiner Elements, etc. It's also easy to do with a programmable keyboard with ZMK/QMK. I've set up my Kinesis 360 Pro this way, being symmetrical means I can access every key easily. Hardware support for sticky keys also helps quite a bit.
- popping the global mark to travel backwards to previous edit points would reuse the same Emacs window instead of using a window that is already showing the buffer. Fixed
- I would accidentally destroy my window configuration w/ C-x 1. Fixed, use advice to automatically save the window configuration into a register if I invoke the command to remove all other windows. Now I can easily recover if I make that mistake.
- I want to be able to select and then scroll any other open window w/o leaving the current one. Fixed
- A crazy one. I collect note w/ links or whatever for reading/watching later, these are marked w/ a timestamp. My notes file is not an agenda file, i.e. not filled with todos / tasks. I made a hack to temporarily include the current non-agenda org-mode buffer in the agenda list and then show inactive timestamps. Now I can scan a day/week/month for interesting notes I took. This doesn't interact at all with my real agenda.
- org-agenda opens items in weird places, use advice to fix it so that it always appears where I like.
- fix inf-clojure so that it uses dep.edn as project root over .git
I used IntelliJ happily for 10 years (I was a heavy Emacs user for 10 years before that). While it's true that some things are a little less convenient (I don't use LSP), knowing that I can tailor things exactly to my tastes is a serious breath of fresh air.It used to be I mostly used IntelliJ for work/OSS and Emacs for org-mode. Now the situation is likely reversed. Emacs for work/OSS and IntelliJ only if I need step debugging/global refactoring.
I started doing this because I got fed-up with Claude Desktop for my StackOverflow style programming questions - and then stumbled upon using it to configure Emacs.
> My experience extending a core emacs function was an instructive and
> interesting exercise. I don't know what the future of emacs looks like in an
> increasingly LLM-crazed world, but I hope that future includes an open and
> powerful way to extend and customize the tools we use to write software.
I have gptel configured with Claude 4.1 via API. Claude generates an org-mode file. I ask it questions about Emacs packages, Emacs configuration, and Emacs customization. It responds w/ Elisp snippets that I can eval immediately and see the effect. Claude knows a lot about Emacs. All these chats are version controlled into git so I can easily pull, consult-ripgrep, and pickup where I left off from any of my machines.I can add my `.emacs` to the Claude context to get more precise answers. If it falls over on some package I can `M-x find-library` to add that library's source to the context. If the code it wrote doesn't work, I add the `Messages` buffer and the `Backtrace` buffer for errors. I eval the snippet, reprompt, rinse and repeat.
With this fast feedback loop (no restarting Emacs, just live coding), I've added a ton of customizations that in my twenty years of using Emacs previously just never felt like I had the time or enthusiasm for given higher priorities:
* Boring stuff: managing where modes open buffers in which windows
* More ambitious stuff: standard org-remark behavior isn't that natural for highlighting and making notes so I made a nicer Transient based thing for it.
* Stuff for work: a fast logging minor mode that font locks incrementally, disables all the save prompting, and handles ASCII color codes. Later I intend to linkify stack traces, linkify data so that they open pretty printed in a different buffer, collect errors and show an unobtrusive notification in the active window, etc. etc.
In two weeks, I've learned more Emacs than I did the 10 years prior. Most of all, this is a usage of LLMs that I can say I honestly love - improving my own day-to-day tools. Because Emacs is a text-oriented live programming environment - LLM integration just feels like it's on a completely different level.
Claude (or any good LLM) + Emacs is a killer app.
Hrm https://mitp-content-server.mit.edu/books/content/sectbyfn/b...
When we eventually remove the direct GCL dependency it will sadly be more for optics than anything else.
While there's obviously plenty to enjoy w/o dipping into the cantatas, you might miss out on making these kinds of connections which I think really enriches our understanding of the instrumental works.
We simply reuse the debug loader provided by Google Closure and load ClojureScript and Google Closure JavaScript files through the Metro server. But this is the core of ClojureScript's hot-reloading capabilities without caveats. In Google Closure namespaces can be represented as nested JavaScript objects which delivers pervasive late-binding - which simply cannot be done with ES6 modules because imports will be captured (early bound).
The REPL bit (which is an independent piece from the hot-reloader) just runs on top of react-native-tcp-socket.
The only tricky part is that we need to be able to require Node libraries and assets into ClojureScript during development. This is done by a compiler pass - first we start at the entry point of the ClojureScript React Native project and follow the dep graph collecting all libraries required from `node_modules`. This is dumped to a file that is required transitively by `index.js`.
Asset handling is done as a simple compiler pass over every AST node searching for JavaScript `require` statements in the ClojureScript.
The end result is that we have an extremely rapid development workflow that simply is not possible with other existing technologies - not React Native, not Flutter, not SwiftUI. All of our apps are built via live-coding from our text editor of choice + REPL (either embedded in IDE or via shell).
Happy to answer any further questions!
Here's the problem.
In Clojure `and` and `or` are just macros that expand to `let` + `if`, simple and elegant:
(defmacro and
([] true)
([x] x)
([x & next]
`(let [and# ~x]
(if and# (and ~@next) and#))))
... except when you target JavaScript - which is not an expression oriented language. So `let` will be wrapped in an immediately invoked function expression (IIFE). Google Closure does elide these when it can, but it will give up after only a few levels of nesting. For an IIFE to appear in the middle of a conditional is a performance killer.Another complication is that it's not safe to just use `&&` and `||` blindly because of `0` and the empty string and the other cases which are not false-y in Clojure(Script). Checks for JavaScript false-y values are a performance killer.
So years ago we implemented a simple form of type inference which annotates the AST with type information as a compiler pass on every node when possible. As long we know the JavaScript `if` will receive a boolean value we can elide the JS false-y value check.
But this is also exactly the information we need to safely run a following pass that looks for the above and/or syntactical patterns and optimize it. As long as each nested `if` is guaranteed to return a boolean, we can remove the local and use `&&` or `||` instead.
The and/or optimization pass ended up being ~120 lines of code with no actual dependencies on anything else in the ClojureScript analyzer or compiler because the ClojureScript AST is just plain EDN - https://github.com/clojure/clojurescript/blob/master/src/mai...
Happy to answer any further questions about this!
UPDATE: Also ClojureScript turned 10 years old this month :)