The “best” debugger I used was a REPL1.
REPL means “Read, Eval, Print, Loop”.
Debugging in a REPL uses the language, itself, as the syntax for the debugger.
Once inside a REPL, a programmer can query (and change) the state of a computation using familiar syntax.
Interpretation vs. Compilation
All programming languages can be interpreted.
Only some languages can be compiled.
Compilation is an optimization, often applied prematurely.
Interpreter and REPL For All Languages Before Compilers
IMO, all programming languages should start out life as interpreters with REPLs.
Then, and only then, compilers can be built (for some languages / language features).
The workflow could be changed to:
- write the program
- debug the program in the REPL
- compile the debugged program for production.
How To Build Interpreters For Existing Languages?
Q: Can Ohm-JS (+ CL, Pyton, etc.) be used to build interpreters for all existing compile-only languages?
Q: Can Denotational Semantics produce interpreters for languages and tie compilers back to the original language semantics? Can the Denotational Semantics of a language be used to produce, both, the interpreter and the compiler?
The REPL happened to be a Lisp REPL, but that is beside the point of this article. I think that pre-CL Lisp was easier to debug than is Common Lisp. OTOH Lispworks (for CL) defines what a debugger should be capable of. ↩