upvote
the benefit of lisp in an editor with integrated repl environment is that you get to see immediate feedback by easily evaluating chunks of your code as you navigate it, without copying stuff back and forth between the editor and the repl

and the benefit of lisp comes from the fact that every expression is fully delineated by parentheses, so you don't need to select any text, find the start or the end of any syntactic construction, you just eval the form you're standing at and see the result

or just as easily you can wrap that form, bind the locals to test values and eval that to see what changes

reply
Almost exactly, it's mostly how you use the REPL that differs, and then only because of what different editors prioritise. When I'm in Emacs, all my work happens against a running REPL - when I open or save a file, it's reloaded. Any tests loaded in the REPL rerun on every save, within that live instance. If I drop into the debugger, it's against that live instance. I can swap in mock components to a running system, go check stuff in a browser (even jack into a live webpage with ClojureScript), all in one long running instance. I have struggled to recreate this kind of setup as smoothly in Python with any editor (pytest doesn't want to run this way, and IPython's autoreload doesn't feel as reliable), but I do probably write more REPLy code in Python than most, so all my model training and optimisation runs during development happen in pausable background threads in IPython etc.

All that said, 90% of the time you still just eval a bit of a code to see what happens and that's the same between the two languages.

reply
I don't know what you meant by pausable background threads in IPython, but if anyone is trying out - I have had some success with VSCode + IPython: https://mahesh-hegde.github.io/posts/vscode-ipython-debuggin... -

(you can theoretically pass "reload": true (or similar option) in launch.json for auto reload, tho I haven't felt the need to use that in my workflows.)

reply
deleted
reply
You evaulate code within your editor against the REPL, seeing the output in the same window you're writing in (perhaps in a different buffer).

The cycle is:

  1. Write production code.
  2. Write some dummy code in the same file (fake data, setup).
  3. Evaluate that dummy code. See what happens.
  4. Modify  code until satisfied.
Your feedback loop is now single digit seconds, without context switching. It's extremely relaxing compared to the alternatives (rerunning tests, launching the program with flags, what have you).
reply
Indeed. For people used to the "typical REPL" from Ruby, Python and alike, the best comparison I've found is this:

"Typical REPL" workflow: Have one editor open, have one REPL open, have one terminal open that runs the application. One change is typically: Experiment in the REPL window, copy-paste into your editor, write tests, restart application (lose all state), setup reproduction state, test change. Or something like this.

In a Clojure REPL workflow, you'd do something like: Have one editor open, this starts the REPL and often the application in the background too. One change is typically: Edit code, evaluate that snippet of code (which sends it to the REPL and the running application), write tests, evaluate them too in the editor, if you're happy, hit CTRL+S and you're done. Application still has the existing state, no restarts needed and you essentially never have to leave the editor window/pane.

Of course, others might have slightly different workflows, but for myself and many (most?) other Clojure developers I've observed in the wild, this is pretty much the standard.

reply
Being someone who’s used to the “typical REPL” flow, I’m not sure I grasp what’s going on with the no-restarts. The implications I think I see are:

* Clojure is built different in terms of hot code reloading

* the REPL is its own application process in languages Ruby or Python, but in Clojure it’s sortof a client for the system

Is that right? Is there more to it?

reply
Yeah, Clojure code is typically built with this in mind, and the data structures as well.

So in a JavaScript server, you might have the database connection set in some config/thing you pass around to request handlers to use, and if you change a request handler, you typically stop the entire application, then start it again. Then the database connection (and the entire config in fact) would need to again connect.

In a Clojure application, first you either start the repl from the server, or start the server from the repl, then you might instead keep the database connection in an "atom" that you keep around for as long as you have the repl/server running. And instead of restarting the process when you change a handler, you'd only change the handler.

And yeah, the libraries and ecosystem at large is built with this (mostly) in mind for everything. The language also supports this by letting you "redefine" basically anything in a running Clojure application, which would be harder in other languages.

I've done some experiments years ago for doing the same in JavaScript, and it kind of works, but every library/framework in the ecosystem is not built with this in mind, so you'll be writing a lot of your own code. Which, now with LLMs, maybe is feasible, but can't say it was at the time exactly.

reply
You're on to something. It's the lisp machine of it all. Hot reloading is nothing that requires anything special, so you can redefine a callback or dependency with ease in the repl and the system chugs along. You can theoretically do something similar in ruby, but it's the opposite of elegant, you'd be forced to re implement methods with different dependencies etc. It's also a function of being "functional" in the lisp sense, that things are lists, and lists can be replaced, functions or otherwise.

The fun way to get a feel for lisp machines is emacs, it's so easy to fall of a language and especially hand-coding in a language if you don't have to.

reply
You don't need a repl for this workflow and it can be easily implemented in any language. `ls *.MY_LANG | entr -c run.sh` You get feedback whenever you save the file.

Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal. When the feedback is very small, it becomes practical to save the file on every keystroke and get nearly instantaneous results with every input char.

reply
> You don't need a repl for this workflow and it can be easily implemented in any language. `ls *.MY_LANG | entr -c run.sh` You get feedback whenever you save the file.

As in restarting the entire program and re-running every subsequent query/state changing mechanism that got you there in the first place, being careful not to accidentally run other parts of the program, setting it up to be just so, having to then rewrite parts if you then want to try something somewhere else?

Perhaps I'm misunderstanding you, because that sounds horrible unless you're scripting something small with one path.

The whole point of the REPL is that you evaluate whatever functions you need to get into your desired state and work from there. The ad hoc nature of it is what makes you able to just dig into a part of the code base and get going, even if that project is massive.

> Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal.

Calling a function on a running program takes microseconds; you're not restarting anything.

reply
Is this similar to Unison scratch file driven development?
reply
https://www.youtube.com/watch?v=5HHLT2_a1tI

An example of me solving an Advent of Code with clojure and repl. You can see i never interact with the repl directly, I just send code to it via my editor and get results inline.

reply
The two big things are:

1. REPL is automatically compiled into running systems 2. Great hot-reloading support

So it's generally very easy to "poke" at a running system, and the whole dev process assumes you will do this.

TBH, these days it is largely possible in a C++ debugger. Less so 10 years ago, though.

reply
it is very similar, but it is easier to evaluate sub-expressions thanks to the unique syntax of lisp.

there's a detailed explanation here: https://youtu.be/Djsg33AN7CU?t=659

reply
> Isn't it more-or-less the same as the Python REPL?

Not even close.

reply
More or less, yes. It's more about the approach to the repl and how it is leveraged in development, or even jacking in to a running system and modifying it as it is running.
reply
[flagged]
reply
Doesn't sound too different from Typescript breakpoints attached to a running website with "Hot Module Replacement."
reply
Yeah to be honest after trying for many years to understand what is so special about this famous Clojure REPL I struggled to see how it was that different in practice from Python or other languages. In Python you can also highlight a section code and send it to the console to be evaluated.

I think debuggers are just better anyways. When I got into the weeds trying to do this interactive REPL workflow on an actually running webapp it was a big mess. You have to write custom code all the time to basically capture data and save it into global variables, so that you can inspect them (as opposed to a debugger where you can just set a breakpoint and inspect all the variables in the middle of the precise spot in the request-response cycle).

I think maybe this was ahead of its time when it came out in 2008 or got popular in 2010-2015, but nowadays I am not seeing what's specifically more productive about the Clojure REPL than the interactive development experiences available in Laravel, Python, JS, etc.

reply
As someone who loves the clojure repl I do somewhat agree with debuggers are better

If it was like a choice, Clojure has my favourite reverse debugger flowstorm which also exposes the runtime information of your program programmatically

So you can code cool visual programs against the runtime values of your first program

Like I built an emulator at work that simulates our program in production as a flowstorm plugin then you can step through the program frame by frame

I love taking the guess work out of production issues, just get the computer to show you exactly how everything went down

It's like a rewindable movie made up of thousands or millions of frames of the execution of your program

reply
To be fair to Clojure, I think it's perfect for rule-engines, where the users supply "business logic" at runtime.

That's where "code is data" shines and you can manipulate it at will. For the rest, I'm glad I have "code as text" in my editor, where I can see and inspect it far more easily than one line at a time.

reply
[dead]
reply