9 ms·
Having never used Common Lisp, it's one of the languages I wish I've used for a long time. However, working with Clojure and Scheme, I understand the power of
by jmkr 5y ago
Having never used Common Lisp, it's one of the languages I wish I've used for a long time.
However, working with Clojure and Scheme, I understand the power of repl driven development that is difficult to explain outside the experience of it.
- threatofrain 5y agoBut repl workflows are not special to Clojure and Scheme. People in JS set breakpoints in their IDE, pause at a point in their script to interactively explore program state all the time. Is this not the same?
- gotts 5y ago>Is this not the same? not even close, no
- joshlemer 5y agoI'm also curious, I think what it comes down to is that Clojure lets you change the code of a running application, rather than reload it.
- jimbob45 5y agoYou can do that with C# in Visual Studio though.
- Nihilartikel 5y agoThe greater Clojure hot-reload super power comes from embracing the idiomatic division of state and function though. Hot reload in C# will have probably have to re-instantiate classes or restart the app. In clj, if your application state is kept in a global atom a'la (defonce app-state (atom {})) Then redefining your functions won't touch your data. The only reason to do a full restart would be if you changed the data structure expected by the functions in a way that's not semantically backwards compatible. Big-whoop on a stateless webserver, maybe, but on the front end using ClojureScript for an SPA it is nothing short of magical and has ruined any other browser dev work flow for me.
- mikelevins 5y ago...and Common Lisp and Smalltalk take it a step further. You can go ahead and change the data structure in a way that's semantically incompatible. The runtime will notice that the definition has changed and either automatically reinitialize any existing instances to conform to the new definition, or, if it doesn't know how to do that, pose a UI asking you to tell it how. And when control reaches a function that expects the old kind of object and runs into an error, it'll enter a breakloop that you can use to modify the now-out-of-date function to handle the new definition properly. Once you've redefined the function you can then tell the runtime to resume and it will proceed as if the function you called had originally had the new definition.
- mumblemumble 5y agoIt is not quite the same. I'd argue that, while browser environments are very good, they're trying to solve a slightly different problem, and the overlap in capabilities is less than complete. Lispers sometimes say that the browser devtools experience is about halfway toward the power of a good Lisp environment, but I would argue that the converse is also true.
- hencq 5y agoPart of it is cultural I think. In Clojure you'll often find yourself building your app incrementally, sending individual functions to the repl from your editor. I don't think there's any reason why you couldn't do that in Javascript. Common Lisp takes this to another level though with stuff like restarts. Where the app will throw an exception during development (perhaps because a function isn't implemented yet) and instead of crashing you'll end up in the repl where you can make some fixes and keep running the app.
- susam 5y agoThere is a nice little story in the book Practical Common Lisp (Peter Seibel, 2005). Quoting it below: An even more impressive instance of remote debugging occurred on NASA's 1998 Deep Space 1 mission. A half year after the space craft launched, a bit of Lisp code was going to control the spacecraft for two days while conducting a sequence of experiments. Unfortunately, a subtle race condition in the code had escaped detection during ground testing and was already in space. When the bug manifested in the wild--100 million miles away from Earth--the team was able to diagnose and fix the running code, allowing the experiments to complete. One of the programmers described it as follows: Debugging a program running on a $100M piece of hardware that is 100 million miles away is an interesting experience. Having a read-eval-print loop running on the spacecraft proved invaluable in finding and fixing the problem.
- CodeGlitch 5y agoI've heard this before and it does sound like the most epic hack in the history of programming.
- e12e 5y ago> 100 million miles That looks like about 9 light minutes away, so about 18 minute latency? It might be super epic, but doesn't sound super nice :)
- Qem 5y agoRecently there was a bit of a fuss when people discovered the Mars Ingenuity helicopter runs python3. Now I wonder, if its software ever crashes, will people be able to just launch the python REPL and debug It?
- _peeley 5y agoNot really. With Clojure you don't even have to leave your editor (I use Emacs with the fantastic CIDER package). You can put your cursor over an S-expression, hit two keys, and evaluate and see the results of the S-expression directly within the text buffer. The feedback loop is so quick it's basically instant - even tabbing over to a browser or terminal window feels lethargic in comparison. I haven't used Common Lisp much, but I've seen a few really impressive demos of debugging in its available REPLs. This is a longer one, but a good tour of SLIME (also an Emacs package): https://youtu.be/_B_4vhsmRRI https://youtu.be/_B_4vhsmRRI
- user3939382 5y agoIn PhpStorm I hit a hotkey to set a break point, then a hotkey to make the API call to that code and immediately get an xdebug breakdown of the entire state of the program at that point. It’s almost instantaneous and I don’t have to switch windows. I would say that’s pretty close, although the upfront learning and config to get that working means it is higher overhead overall.
- slifin 5y agoFor context I use phpstorm and Clojure Here is a video I recorded which hopefully demonstrates just how much faster you can go in Clojure https://youtu.be/L0af0bc5Jec https://youtu.be/L0af0bc5Jec Like I think a parent comment said it's hard to explain but once you get it, it's fun malable and exploratory Intellji also has a debugger that works with Clojure for when it's appropriate In php you can't redefine a function at runtime without something like runkit, in Clojure you can fix your systems at runtime
- phyrex 5y agoIt really isn’t and I suggest you give clojure an honest try to see why. It’s really hard to explain.
- patrec 5y agoMikel Evins has written two articles you might find interesting: https://mikelevins.github.io/posts/2020-12-18-repl-driven/ https://mikelevins.github.io/posts/2020-12-18-repl-driven/ https://mikelevins.github.io/posts/2020-02-03-programming-as-teaching/ https://mikelevins.github.io/posts/2020-02-03-programming-as...
- capableweb 5y ago> I understand the power of repl driven development that is difficult to explain outside the experience of it. I've been having trouble with this as well. I think we'd do best in removing "REPL" from any explainations, and instead use the words "Interactive Development", "Dynamic Development", "Hot Reload on Steroids" or something similar, as when I use terms like that, people understand it's nothing like the "repls" ("shells" really) other languages provides.
- lispm 5y agoThe REPL is the nucleus of interactivity in Lisp. The first Lisp, called LISP I and its successors, from John McCarthy and his team had a remarkable collection of innovative technology: * a reader for symbolic expressions. These are nested lists of symbols and other data objects like numbers and strings * a printer for symbolic expressions. * an evaluator for symbolic expressions. Each symbolic expression evaluates to a value. Side effects to the running Lisp could be added variables, data or function definitions. * an automatic garbage collector taking care of freeing no longer used memory * piece-by-piece allocation of data * a form of programming with functions, also using recursion * a way for Lisp to store and restore the main memory heap to/from external storage * Lisp compiler written in Lisp, which generates assembler, and an assembler which creates code into the runtime -> the first interactive incremental to memory compiler * a way to program with Lisp code generated by Lisp macros It had a batch REPL. This Read-Eval-Print-Loop would read a file of expressions, expression by expression, evaluate them and print each return value into a file. It used the above functions READ, EVAL and PRINT. Lisp was starting up and restoring a default image as the heap. It would then execute the file via the batch REPL and save a new, changed image as the new default image. Next time Lisp starts, it would then restore the latest image. A next step in the evolution was then to connect a terminal and let the user enter a Lisp expression. Lisp reads it, evaluates it and prints the value back. Side effects again might change the running Lisp. The REPL then waits for the next terminal input. Code could also be loaded from files. That way the REPL was not some interface which the debugger brings up on request. It was the main interface for the programmer to build up large Lisp systems piece by piece. We are talking about the early 60s here. In the coming decades the idea of a REPL morphed into extensive interactive resident development environments like Interlisp <http://interlisp.org http://interlisp.org>. Interlisp combined the REPL with managed source code, structure editing for code and data, automatic error correction of code, undo of evaluation effects, a virtual machine, ... and a lot more. Here Lisp, the IDE and the programs under development were running in one Lisp runtime. We are talking about the early 70s. Interlisp moved at Xerox PARC onto their new graphical workstations. Thus the REPL-based development environment moved to graphical user interfaces. The Interlisp team got an ACM Award for their pioneering work. The MIT AI Lab took that idea to their graphical Lisp Machines. Today in 2021 Lisp programmers often use SBCL as the implementation, GNU Emacs and SLIME (or similar) as their development environment. It's still usual to develop a running program, having potential multiple REPLs into the program, using Emacs/SLIME to send code for remote evaluation to the running Lisp. GNU Emacs itself is a Lisp program, too - with its own integrated Lisp and the development tools for it. GNU Emacs has its own REPL, called IELM, the interaction mode for Emacs Lisp. Even in Lisp not all development interactions goes through a REPL, but there are still a few very powerful ones, like those of Allegro CL, LispWorks, McCLIM, Interlisp (recently open sourced) or the Lisp Listener from Symbolics Genera. Typical is the rich interactive error handling in multilevel break loops with restarts and the ability to continue from errors. The above also support image-based development and integrate Lisp, IDE and the software under development in one program.
- throwaway894345 5y ago> However, working with Clojure and Scheme, I understand the power of repl driven development that is difficult to explain outside the experience of it. The only lisp I've dabbled with was Racket and I found the repl driven development to be a frustrating necessity. I would spend a lot of time trying to figure out the actual type of the thing that I a given function (even a stdlib function) needed, and futzing around in the repl seemed to be the fastest way to do it, but it was still quite slow. It reminded me a lot of my extensive experience with Python where things that are easy in, say, Go, are quite hard. People rave about their repl, but it feels like they're comparing it to a script iteration loop rather than static analysis tooling. That said, I completely buy the argument that Python's REPL is particularly bad and that there are other use cases where REPL-driven development shines. My mind is open, but these are the sorts of experiences the pro-REPL folk should be prepared to engage with in their evangelism. :)
- ReleaseCandidat 5y ago> People rave about their repl, but it feels like they're comparing it to a script iteration loop rather than static analysis tooling. You do know that statically typed languages have REPLs too? Like the ML family, including Haskell. And when using something like a Jupyter notebook with a kernel for your compiled language https://github.com/gopherdata/gophernotes https://github.com/gopherdata/gophernotes you can do similar interactive programming. REPLs are about trying out ideas or changes. Lisp REPLs take that a step further, as you interact with and in your whole actually running program.
- throwaway894345 5y ago> You do know that statically typed languages have REPLs too? Like the ML family, including Haskell. I do, but that I don't see how that relates to the bit of my post which you've quoted. I certainly didn't claim or imply that REPL and static type systems were mutually exclusive, only that REPLs are a poor substitute for many static analysis tasks. > And when using something like a Jupyter notebook with a kernel for your compiled language https://github.com/gopherdata/gophernotes https://github.com/gopherdata/gophernotes you can do similar interactive programming. Yeah, I'm aware. I operate a large JupyterHub cluster (among many other things) at work. :) > Lisp REPLs take that a step further, as you interact with and in your whole actually running program. That sounds nice, but it's too abstract to persuade IMHO. Personally, it seems like REPLs can make certain feedback loops a bit faster, and to that extent they seem like a modest quality of life improvement; however, considering how lisp folks positively rave about them, I assume I'm missing something.
- Qem 5y agoThat happens with languages in the Smalltalk family too, like Pharo. It's hard to convey the convenience of the Transcript window and the while environment without actually showing ir experiencing it.