The sly debugger is better than most debuggers, but calling it the most powerful debugging experience is a stretch. Just try the debuger in a Smalltalk implementation (Squeak/Pharo/Cuis)
Saying that you just have to watch a bunch of tutorials betrays just how janky it is.
For example, in CL debugging, how would I reliably set a breakpoint at some nested expression in a function body, run to it, and step through the code "expr-by-expr" while verifying a bunch of values in a watch window? In VS, it's one click to set breatpoint, one keypress or two clicks to run to it, one click to step to next line, and one double click and typing an expression to set up a watch that would show updated value of that expression every time execution pauses while taking care of scoping, etc.
This is a common complaint about CL, but my experience is that the utility of debuggers is vastly over-stated. It’s a nice trick in Java or C/C++ or JS (in Chrome), but I've never found a debugger to significantly improve my workflow and the “repl-driven development” paradigm of CL solves the problem differently by increasing iteration speed.
Having to modify code to add/remove breakpoints is pretty much the definition of jankiness. Can Emacs+SBCL show a list of all breakpoints, for example?
> This is a common complaint about CL, but my experience is that the utility of debuggers is vastly over-stated.
I disagree. A debugger gives you insight into the state of the program. It makes no sense to burn neural cycles trying to track program's state manually when a debugger can visualise it in multiple views and forms. Frankly, during development, I always run my code in a debugger, just to be able to confirm my assumptions on a granular level whenever I want to.
> the “repl-driven development” paradigm of CL
In my experience, trying to substitute a proper debugger with REPL leads to unnecessarily small functions, which then lead to very deep call stacks and undocumented assumptions about pre- and post-conditions of all those tiny functions. However, CL is specially bad about this anyway, since every `let` binding adds a new nesting, making long linear functions unreadable (either due to deep nesting, or due to predeclaring all variable as in ANSI C). Scheme with its `define` is much better in this regard.
Sly's UI seems to be similar to that of GDB, which is exactly what I am talking about. I would suggest trying out and comparing GDB and one of its numerous GUI frontends to understand why a GUI with multiple views displayed simultaneously (i.e., you can keep track of different parts of state on different parts of screen) and automatically (i.e., display doesn't require interaction) improves debugging experience.
As regards windows and gui, I also dont understand. I can certainly have multiple windows displayed in Emacs, even in a tty terminal. What buffer each of those windows serves is up to me. Certainly I can have each state sent to a different buffer. One of the things I personally value about Emacs is that I don't have to run it in some Windowing gui system to get any of the supposed benefits. Heck I can even use a mouse to adjust window sizes in just my tty. I dont get what GUI gives you except a higher overhead
https://learn.microsoft.com/en-us/visualstudio/debugger/debu...
There is no feature here that is not available for debugging Common Lisp in Emacs, which has much more powerful debugging tools, and with less overhead
For many Common Lisp implementations you don't need that. The debugger & source interpreter & compiler & break loops are always available. One also does not need to "instrument" code for a debugger.
> In my experience, trying to substitute a proper debugger with REPL leads to unnecessarily small functions
That makes no sense. The REPL includes a debugger and does not enforce a function size. You can call&debug functions of any size from a REPL. Stack traces can be detailed or overview. From within the REPL, which in case of break is a break-loop, which is a REPL with added debug commands.
> undocumented assumptions about pre- and post-conditions of all those tiny functions
The idea of tiny functions is made up by you. Common Lisp's debugging tools don't enforce tiny functions. Just the opposite, many CL code bases have large functions, which are also even larger after macro expansion.
pre- and post conditions can be explicit parts of Common Lisp code, since it has :before, :around and :after methods.
> every `let` binding adds a new nesting
Lisp uses nested lists. That's one thing to learn from day one when programming in Lisp. Nested code shows indented blocks.
> or due to predeclaring all variable as in ANSI C)
Not predeclaring variables is bad practice anyway.
> Scheme with its `define` is much better in this regard.
No, Scheme's "define" has to be at the top of a block, according to Scheme standards.
It is possible to patch a running CL system by simply setting a breakpoint and then revaluating a function.
I am unaware of any other language that is possible in. This Lisp feature is only made possible through a debugger.
I know, because I've done it.
It's also possible to change a running CL system without setting a breakpoint and/or re-evaluating a function.
> I am unaware of any other language that is possible in.
Start with Smalltalk and then search for more.
The function FOO has a BREAK.
CL-USER 76 > (defun foo (a)
(+ (break) (sin a)))
FOO
CL-USER 77 > (foo 10)
Break.
1 (continue) Return from break.
2 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
We are in the break lets get a quick backtrace and move to the right stack frame: CL-USER 78 : 1 > :bq
INVOKE-DEBUGGER <- BREAK <- FOO <- EVAL <- CAPI::CAPI-TOP-LEVEL-FUNCTION <- CAPI::INTERACTIVE-PANE-TOP-LOOP <- MP::PROCESS-SG-FUNCTION
CL-USER 79 : 1 > :n
Call to INVOKE-DEBUGGER
CL-USER 80 : 1 > :n
Call to BREAK
CL-USER 81 : 1 > :n
Interpreted call to FOO
What is the current source? CL-USER 82 : 1 > :lambda
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 8220104A8B>)) (DECLARE (LAMBDA-NAME FOO)) (+ (BREAK) (SIN A)))
Let's change the code: CL-USER 83 : 1 > (setf (third (fifth *)) '(* a 4))
(* A 4)
Let's see the new code. CL-USER 84 : 1 > :lambda
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 8220104A8B>)) (DECLARE (LAMBDA-NAME FOO)) (+ (BREAK) (* A 4)))
Move to the BREAK stack frame and return 2 from there: CL-USER 85 : 1 > :p
Call to BREAK
CL-USER 86 : 1 > :ret 2
42
As you can see, we have changed the running source code, without the need to restart anything.> For example, in CL debugging, how would I reliably set a breakpoint at some nested expression in a function body, run to it, and step through the code "expr-by-expr" while verifying a bunch of values in a watch window?
I ... don't ever do this when debugging lisp? Sly has support for setting breakpoints, but I'm used to adding (break) expressions; the "breakdot on the side" doesn't work as well with Lisp as C (With idiomatic C you very rarely want to set a breakpoint in the middle of a line, and it's rare for a single statement to be more than 2-ish lines; I should also note that IIRC setting breakpoints in the middle of a line in VS was not the best UX).
Sly has a keyboard shortcut for stepping, but I don't have it memorized because I hardly ever use it. Conversely its F10/F11 in VS, which I still remember despite not having used Visual Studio for at least a decade, since I did it a lot!
Conversely, I have the shortcuts for recompile the single file I'm in and recompile the single function I'm in memorized, since I use those all the time when debugging. I believe VS added hot-patching functions at some point near the end of my usage, but it wasn't part of my workflow like it is with Lisp.
(The lack of) watch windows is a deficiency in Sly. While you can view all locals in the current stack-frame while debugging, it doesn't let you watch arbitrary expressions, and if you have too many locals it can be hard to focus on what you want.
I should point out stickers (C-c C-s C-s) let you trace evaluations of arbitrary subexpressions, and this has some overlap with how watchpoints are used in VS.
You can't watch expressions, but when the debugger comes up, and I think if you're stepping through too, you can hit "e" for eval and do any expression you basically want. Any variables used would need to be in scope, etc ...
(this is with sbcl and slime ... I'd bet sly supports similar)
Just look at the screenshots. What's missing? Literally everything is missing, because it's just a CLI in a window. Want to add a breakpoint? Type in the window. Want to know how many threads are running? Type in the window. Want to pause a thread? Type in the window. Want to inspect a variable? Type in the window. Want to drill down through a complex data structure? Type that 50 character line correct in one go bitch. By god! Don't you dare complain!
The irony of course is that all of these commercial products are using the exact same debugging infrastructure of the OSS counterparts. It's just that one group finished the job, and the other doesn't bother to finish the job.