Comment by kragen

8 hours ago

> You’ve likely used powerful coding editors and terminals. Smalltalk is just different. You are in the running environment.

I've experienced this a few different times: with Microsoft BASIC-80 (and GW-BASIC), with SBCL and SLIME, with LOGO, with GForth, with OpenFirmware, with MS-DOS DEBUG.COM, with Jupyter, and of course with Squeak. It really is nice.

It used to be the normal way of using computers; before memory protection, it was sort of the only way of using computers. There wasn't another memory space for the monitor to run in, and the monitor was what you used to do things like load programs and debug them. This approach continued as the default into many early timesharing systems like RT-11 and TENEX: there might be one virtual machine (memory space) per user, but the virtual machine you typed system commands into was the same one that ran your application. TENEX offered the alternative of running DDT (the debugger) in a different memory space so bugs in the application couldn't corrupt it, and that was the approach taken in ITS as well, where DDT was your normal shell user interface instead of an enhanced one.

All this seems very weird from the Unix/VMS/Win32 perspective where obviously the shell is a different process from your text editor, and it's designed for launching black-box programs rather than inspecting their internal memory state, but evolutionarily it was sort of the natural progression from a computer operator single-stepping a computer (with no memory protection) through their program with a toggle switch as they attempted to figure out why it wasn't working.

One of the nicest things about this way of working is halt-and-continue. Current versions of Microsoft Visual Studio offer sometimes halt and continue. In MBASIC you could always halt and continue. ^C halted the program, at which point you could examine variables, make arbitrary changes to the program, GOTO a line number, or just CONT to continue where you'd interrupted it. Smalltalk, SLIME, or ITS allows you to program in this way; if you like, you can refrain from defining each method (or function or subroutine) until the program tries to execute it, at which point it halts in the debugger, and you can write the code for the method and continue.

This is an extremely machine-efficient approach; you never waste cycles on restarting the program from the beginning unless you're going to debug program initialization. And in Smalltalk there isn't really a beginning at all, or rather, the beginning was something like 50 years ago.

Myself, though, I feel that the hard part of programming is debugging, which requires the experimental method. And the hard part of the experimental method is reproducibility. So I'm much more enthusiastic about making my program's execution reproducible so that I can debug faster, which conflicts with "you're in the running environment". (As Rappin says, "Code could depend on the state of the image in ways that were hard to replicate in deploys." I experience this today in Jupyter. It's annoying to spend a bunch of time trying to track down a bug that doesn't exist when you restart from scratch; worse is when the program works fine until you restart it from scratch.) So I'm much more excited about things like Hypothesis (https://news.ycombinator.com/item?id=45818562) than I am about edit-and-continue.

Paul Graham wrote somewhere (I can't find it now) about how in Viaweb's early days he would often fix a bug while still on the phone with the customer who was experiencing it, because he could just tweak the running CLisp process. But you can do the same thing in PHP or with CGI without sacrificing much reproducibility—your system's durable data lives in MariaDB or SQLite, which is much more inspectable and snapshottable than a soup of Smalltalk objects pointing to each other. (#CoddWasRight!) Especially since the broad adoption of the Rails model of building your database schema out of a sequence of "migrations".

I found the Smalltalk way of working in the running environment to be very programmer efficient too, and that it was by far the smoothest development experience I’ve had, even in a pretty dated and clunky Smalltalk at that point. And debugging wasn’t really a problem in my experience, but we stored application state outside of the image in an SQL database (initially Sybase, then MSSQL), which probably removes some «haha, the image has some weird data saved in the dark and dusty corners» issues.

> But you can do the same thing in PHP or with CGI without sacrificing much reproducibility

PHP is similar, but not the same. You can't (or at least I can't) stop a request in progress and change its code; but you can rapidly change the code for the next request. Make a change in the editor, hit reload in the browser is a productive short loop, but stop at a breakpoint, inspect the state and change the code is a more powerful loop. Stopping at a breakpoint is challenging in systems with communication though, and I've learned to live without it for the most part.

  • Yes, I agree. I meant that you can edit code on the production web server so that the customer you're on the phone with sees their application start working. PG had the advantage that he could tweak the Lisp process for that specific user, reducing the risk of breaking the site for other concurrent users.

    Database transactions bridge some of the gap between "change the code for the next request" and "stop at a breakpoint and change the code": as long as your request handler code keeps failing, it will abort the transaction, so the database is unchanged, so you can restart the transaction as many times as you want to get to the same point in execution, at least if your programming language is deterministic. By providing a snapshot you can deterministically replay from, it allows you to add log entries before the point where the problem occurred, which can be very useful.

    Stopping at a breakpoint can be more productive, especially with edit-and-continue, but often it isn't. A breakpoint is a voltmeter, which you can use to see one value at every node in your circuit; logs are a digital storage oscilloscope with a spectrum analyzer, where you can analyze the history of millions or billions of values at a single node in your circuit.