> Did I miss something important that should be covered?
Accessibility. Outside of that everything seems reasonable. But I don't know much about cross-platforms GUI. I expect someone will come and explain that Delphi or something like that already does everything the author wants except for the integration with Clojure.
I would add that I'm not sure which value Clojure provides here. Does the REPL adds anything to the live-reload environment? If not, the choice of Clojure seems to be arbitrary.
live-reload or hot-reload I've seen usually refer to systems that preserve state in a running application (so you're not reloading from scratch). So your application never actually stops running when new code is loaded and you get the same sensation that you're editing the application "in-place."
That being said even in that environment having a REPL is nice (although not as much of a quantum leap so much as an incremental improvement) because you can use code to directly poke at your application but is not actually used by your application instead of only being able to write code that your application actually uses.
I think they are very similar, but different enough that it makes sense to use different words. Using just the word "REPL" doesn't give much insight into the iceberg of capabilities that lisps like Clojure, Common Lisp, etc. provide. I wrote a long post about some of the differences: https://news.ycombinator.com/item?id=28475647
Would love a better word or phrase to differentiate from e.g. the way people experience a python or haskell REPL.
For the richer version others have suggested "hot reloading", "live reloading", "live patching", etc. I think all of these are descriptive enough, and most importantly, don't overload a term that many people already understand to mean something different
I agree that the term "REPL" is poorly chosen, since read-eval-print-loop equally applies to a Bash prompt. But in this case, it's even more than hot reloading, because you are making changes in the running application, not just in the saved source code.
With hot reloading, you can change the appearance or behavior of a button and see it reflected immediately. With a Clojure REPL, you can do that but also load in different data, trigger actions, etc.
Then it's possible an even broader term is needed, but "REPL", to non-lispers' minds in 2021, means narrowly "a prompt where you can type code and then hit enter and see a result". I think "hot-reloading" would be a dramatically more descriptive term even if it isn't perfectly descriptive.
Yes, I've been saying "live interactive development" instead of REPL more recently, and I think it conveys the idea much better. Since in Lisps, a REPL means:
> a live integrated inside an application REPL that lets you evaluate and hot-swap every single line of code at will and on-demand all at runtime without ever needing to restart the application, while also being able to inspect the runtime state at will
We do have a term for it, it's Image based programming. The primary means of interacting with said image is quite often the REPL, and so the two may have become synonymous.
To me that term also implies that images are the primary means of distribution, as in (most) Smalltalks. That said, it's still probably a better term than REPL for the Clojure experience.
Yes, didn't mean to imply it was unique to Lisp. And when there is little distinction between what you distribute and what you develop in then it's very apt.
Right, but there is a distinction in Clojure between what you distribute and what you develop. You aren't shipping an image, you're shipping compiled source code.
Modern hot reloading and how lispy REPLs work are similar in many ways, i.e. they allow you to make changes to a running application without stopping it, however there are differences in their experience and how they accomplish it.
The primary difference in experience is the unit of change that you can make with these systems, which is typically based on the unit of compilation of the language you're using.
For example if you're using a language whose unit of compilation is a file, IME you must reload every change in the file at once. This has upstream effects to how hot reloading is accomplished, because if you're reloading an entire file where you've only _actually_ changed one function, then your hot reloading system has two options:
1. It reloads everything in the file, meaning you're potentially losing state that could have been preserved across the change. This typically leads developers to spread definitions across many files in order to be able to make more fine grained changes to their running system.
2. It uses some heuristic to detect whether state should be preserved or not, typically via some static analysis. This can be opaque to developers working on applications, and adds a lot of complexity to the hot reloading system, but is probably the best one can do if your unit of compilation is a file.
Lisps, on the other hand, typically make the unit of compilation a "form", i.e. a well-formed syntax tree like (foo (bar baz)). This, combined with some editor tooling, means that I can make extremely fine grained changes to the system by selectively evaluating forms within a file, preserving its state except for the form that was evaluated.
The way this works in practice is: I have my editor open and connected to my running application using an extension. I place my cursor on a form I have changed and use a hot key to compile and send that form to the running application. I can view the return value of that form in my editor, and if there were any side effects (e.g. re-defining a variable or function), my application will now exhibit that new behavior.
Side note: the benefits of being able to evaluate forms and see their output in my editor is hard to state. It turns your editor from a one-way communication tool (I make changes, hit save, and those changes get made to the application) to a bidirectional tool, where I can both change and inspect the running system. I wish more languages had robust tooling for this.
The other difference that you tend to see between languages (even between languages with just hot reloading) is late vs. early binding. Lisps are typically very late bound, so you don't need to recompile and/or restart your entire application on every change.
Other languages who bind earlier have to deal with stale bindings. Even JS hot reloading has to deal with this problem in complex ways because most bundlers use closures as modules and bind their inter-module dependencies on instantiation, which means that even though you in theory don't need to recompile or reload anything except the one JS function you just changed, in practice you have to reload the file and any files that depend on it, which puts us back in the position of needing to use some sort of static analysis to detect what has actually changed in order to precisely invalidate application state.
I hope this mountain of text helps highlight the subtle differences between hot reloading and a lisp REPL. It's not just the tooling but the specific semantics of the language that make a difference when trying to make changes to a running system.
Source: my experience working with and developing tools for hot reloading and REPL development in JS, ClojureScript and Clojure
The work is funded by a Clojure community group (and a Clojure-using company), and Tonsky has written a bunch of awesome other Clojure (see DataScript[1]).
So it seems as though the decision to use Clojure was not made for technical reasons, but it's not exactly arbitrary.
Live reload gets you most of the way for UI programming, yes.
REPL is the secret sauce for writing any time of program, not only UI. It lets you explore, play, test, inspect and break things very quickly and without losing context before you find the final shape of your code.
I can’t live without it, but it’s hard to explain the benefits to the people who have never experienced it. You have to try it to really be convinced
Accessibility. Outside of that everything seems reasonable. But I don't know much about cross-platforms GUI. I expect someone will come and explain that Delphi or something like that already does everything the author wants except for the integration with Clojure.
I would add that I'm not sure which value Clojure provides here. Does the REPL adds anything to the live-reload environment? If not, the choice of Clojure seems to be arbitrary.