Where does testing fit into programming by teaching? I've worked with people who write some code, refresh the screen, write some more code, refresh the screen, etc. Is that programming as teaching? They definitely produce at least 10x more code than I do, but it is instant legacy code with no tests. Do you go back and write tests after everything looks like it is working?
Other comments have pointed out that test-driven design was actually born in the Smalltalk community, which sort of answers your question, by implication, at least: it's standard operating procedure to write a lot of tests as you go.
Indeed, in my experience, any expression I write in the repl is more likely than not to be a test, or part of a test.
The way I normally work is with a running Lisp image and one or more files open in editors. For greenfield development I usually create a project and write a loadfile to load any library dependencies I expect to use. Once that's loaded, I write some expressions to build some test data, and then some more expressions to operate on the test data. I evaluate the test-data constructors and then fiddle with operations on the data to assure myself that what happens is what I expect to happen. Basically, I start with an idea of how to accomplish something, sketch out the data structures and APIs I think I'll need, and start exercising them to see what I got wrong, what I need to add or change.
The contents of those source files gradually become the source code of my program and of the test suite that go with it. When the project is new and small, the sources are a few files with a small number of lines of code, and the organization is minimal and loose. As I flesh out the program, the source files grow more numerous and more formally organized.
As a project gets complex, I may choose to save working images at points where I'm confident that the state of the system is good. Such images enable me to start the Lisp environment with all of my code already loaded and ready to use. I can then work from that point in the same way that I've described.
The normal way I work on anything, unless the tools I have to use prevent it, is to have a work-in-progress running, and use variable references and function calls in the repl to query the and modify the dynamic state. For any expression more complicated than a short one-liner, I actually write it in a source file and copy it to the repl (actually, I usually use Emacs with SLIME, so "copying it to the repl" really means typing a keystroke or two in the source file).
One of the differences between old-fashioned Lisp and Smalltalk systems and newer systems with repls is that the old Lisp and Smalltalk repls are capable of doing every part of program development--defining and redefining functions and data structures, running and debugging code, inspecting dynamic state, catching, inspecting, and modifying erroneous code, and building the finished app for deployment--all by typing expressions at the repl.
To put it another way, the entire language and development tools and all of your application's internals are always in memory and completely accessible from the repl.
It's not that you would normally do all your development in the repl, although you could if you wanted. Rather, the important point is that, because the repl can do anything the language and development tools can do, there are no artificial limitations on what you can do from the repl. Outside old Lisp and Smalltalk systems, every repl I've used always imposes some restrictions--some things you can't do from the repl. Invariably the way I discover this is by needing to do something in the repl and finding that it won't let me.
Thanks for the detailed explanation. I haven't used smalltalk, and I think making the mental shift from "source files and an interpreter or compiler" to "working in the image" is something I'll need to experience to fully appreciate.
It's easier to grok if you have a friend or colleague who is familiar with it and who can show you the ropes.
Also, there's no guarantee that you'll find it congenial. Some people profess to prefer programming-by-carpentry, and that's fine, of course.
In fact, maybe it's better for your sake if you don't like it. The great majority of programming work is programming-by-carpentry. You might find that you really like programming-by-teaching, and then spend the rest of your life pining for the opportunity to work that way.
Smalltalk is the home of a very early unit test framework, SUnit, which generated quite a few other frameworks. Kent Beck described it in his book Kent Beck's Guide to Better Smalltalk along with an earlier paper. http://swing.fit.cvut.cz/projects/stx/doc/online/english/too...
Test suites and test cases.
Tests are usually written first and drive the development.
When encountering a non existing message, the debugger pops up and asks you if you want to create the message.
Usually, this makes one think: "ha, this is not right" and then goes back on the execution stack and either fixes the test and continues or refactors something etc.
But in a dynamic language, tests are required to avoid regressions.
Programming as teaching goes both ways, programmer is teaching Smalltalk, Smalltalk helps in revealing thinking flaws to programmer. Loop continues.