|
|
|
|
|
by jaen
25 days ago
|
|
I love REPL-driven development, and exploratory "programming" via small snippets is likely part of the endgame (the more the agent strays outside its comfort zone, the more it's needed). It also looks like it paradoxically saves context, since piling up many small snippets is still better than trying to fix a one-shot gone horribly wrong. And yeah, if Clojure had a better static safety story, it would actually rank high, since it's high-abstraction with metaprogramming capabilities, excellent runtime specifications, and has a good "garden path" ie. natural code is good code. (As a devil's advocate though, REPLs can be replaced with gluing together scripts with ad-hoc state/caching via the file system, which LLMs seem to be pretty good at already...) So as an addendum, introspectability is also important ie. allowing to discover the state and composition of the system at any moment - though that's partially a language (eg. reflection) and partially a tooling (eg. debuggers) issue. |
|
Here's one of many practical examples I can give you - my WM on Mac is Yabai, it is hooked up to Hammerspoon, which can be scripted with Lua, which means I can use Fennel, which means I can have Lispy REPL. And from here, it is a bit difficult to explain the difference. The challenge is that the magic is invisible to people who haven't felt it.
The key insight is "the image vs the file". In most languages, your program is a description that gets turned into a running thing. The REPL is bolted on - a convenience wrapper around that same lint/compile/run/restore-the-state cycle. Python, Ruby, Lua, C#, etc. REPLs work that way. You're still fundamentally working with files that produce processes.
In a Lisp, the running system is the environment. There's no gap between "the code" and "the live thing". When I connect to Hammerspoon's Lua runtime via Fennel, I'm not sending scripts to a subprocess - I'm reaching into a living system and reshaping it while it runs.
The missing vocabulary here is "liveness", not "fast feedback" - that's a pale shadow of it. Liveness means the environment has no opinion about what's "done" versus "in progress". Everything is always mid-flight and accessible.
So I can actually reach out in live REPL session to let's say Slack app window and extract the data about every single element in the app, get the content, compare and continuously reiterate, without having to restart anything, without even saving the code - just pure data extraction without compiling, dealing with state changes, etc. I can interactively move the window, resize it, hide, or maximize it - all that programmatically. Imagine DevTools on steroids, only it works for everything, not just web apps.
Learning Lisp and Clojure allowed me to truly experience the genuine joy of programming, because it makes it feel like you're playing a video game. And now, can you even imagine what happens when you open up access to all that awesomeness and grant it to an LLM? Most people have zero idea what it feels and looks like, when you can point an agent to a REPL running in a k8s cluster and it introspects things on the fly, while you let another agent poke through the UI and they work as a team to fix something or develop a new feature.
> if Clojure had a better static safety story, it would actually rank high
This framing reveals an assumption - the primary value of a type system is catching errors early, and that Clojure is just a dynamically typed language that would be improved by adding that. In a Lisp image, "early" and "late" barely exist as meaningful categories. The feedback loop isn't compile-time vs runtime - it's just... now. You evaluate a form and you know immediately. The error is right there, in context, with the live data that caused it.
Static types are, in a real sense, a compensation for the gap I just described - the gap between the description and the running thing. When you can't easily inspect or reshape the live system, you want the compiler to tell you as much as possible before you cross that gap.
Clojure has Spec (which can do things most other type systems would struggle to express), it has instrumentation, it has a rich data inspection story. You said "debugger", Clojure has Flowstorm, which is one of the best debugger experiences I have ever encountered in any language, and I have used more than a few.