Most of my development is done in my own Lisp dialect, which is quite similar to Common Lisp. I use any of five different methods. (4) is the most interesting, and very useful. Does anyone else use it or something similar?
(1) Just add debug statements near where the bug is happening. These print a string, and the name and value of variables. Printed values in Lisp are human-readable, not just a pointer.
(2) Trace selected functions. This outputs the function name, arguments, and return value on function entry and exit.
(3) Use the virtual machine debugger. I can set breakpoints, display the stack, and trace VM instructions, but it's most useful for printing out disassembled compiled code.
(4) Browse data structures in Firefox. While my Lisp is running, a web browser runs in another thread, and every symbol has its own URL. Data structures are displayed as HTML tables.
(5) Unit tests. I've used these to debug complex algorithms, e.g. for event handling, and type inference.
I wrote a version of (4) a few weeks ago for convenience while doing some clojurescript work. I suspect yours is more helpful to you because "knowing state" isn't as useful when you're trying hard to minimize your use of it.
I've never heard of viewing your data structures in a web browser, that's pretty wild. With something like Visual Studio, you can look at data structures in a tree view though.
(1) Just add debug statements near where the bug is happening. These print a string, and the name and value of variables. Printed values in Lisp are human-readable, not just a pointer.
(2) Trace selected functions. This outputs the function name, arguments, and return value on function entry and exit.
(3) Use the virtual machine debugger. I can set breakpoints, display the stack, and trace VM instructions, but it's most useful for printing out disassembled compiled code.
(4) Browse data structures in Firefox. While my Lisp is running, a web browser runs in another thread, and every symbol has its own URL. Data structures are displayed as HTML tables.
(5) Unit tests. I've used these to debug complex algorithms, e.g. for event handling, and type inference.