I fear Lisp had already a better debugging experience in the mid to late 60s...
Notes on debugging Clojure code
11–20 of 50 posts
Re: Notes on debugging Clojure code
#12Another trick is to define temporary inline vars. If I have some Ring handler, it can be quite annoying to construct a request by hand. Instead, I can do something like: (defn foo-handler [{{:strs [authorization]} :headers :as req}] (def *req req) (def *auth authorization) ...) Then fire off a request in my browser and simply proceed to play around with `req` and `auth` in my editor/REPL, which is where I am running…
This is a terrible advice, and is not even necessary. Even println debugging is better than this. Not only defs are abused contrary to their intended use, they are difficult to distinguish from legitimate defs, and thus some may (and will!) slip away into the wild.
Print debugging is great. A carefully placed print statement is something many great programmers swear by [1], despite the existence of "sophisticated" debuggers (see footnote in OP). This is a complement to this when you want to play around with the data directly.
> Not only def's are abused contrary to their intended use, they are difficult to distinguish from legitimate defs, and thus some may (and will!) slip away into the wild.
Never been an issue for me. It's just an extension and morphing of playing around with data in a standalone REPL. The temporary defs are never used in actual code logic, the prefix character makes it clear it is purely for exploration. Is it a dirty hack? Yes, but one might argue that's partly what Lisp is too: The most intelligent way to misuse a computer - Dijkstra (paraphrasing) Lisp
Some unsolicited feedback: you might want to consider that there is more than one way to skin a cat. If you want to understand the trade-offs of solutions people find, you might find it useful to not dismiss solutions outright. Especially something as universally useful like print debugging, considering how many people use it. Instead, a more useful question to ask yourself is: why do experienced programmers use print debugging when they are aware of the existence of debuggers? That might expand your worldview, as opposed to categorizing these people as "doing it wrong". See Worse is Better etc.
1: From this lovely quote by Brian Kernighan: The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.
Re: Notes on debugging Clojure code
#13Another trick is to define temporary inline vars. If I have some Ring handler, it can be quite annoying to construct a request by hand. Instead, I can do something like: (defn foo-handler [{{:strs [authorization]} :headers :as req}] (def *req req) (def *auth authorization) ...) Then fire off a request in my browser and simply proceed to play around with `req` and `auth` in my editor/REPL, which is where I am running…
This is a terrible advice, and is not even necessary. Even println debugging is better than this. Not only defs are abused contrary to their intended use, they are difficult to distinguish from legitimate defs, and thus some may (and will!) slip away into the wild.
I think a lot of the constraints can be relaxed during the development phase.
Re: Notes on debugging Clojure code
#14I fear Lisp had already a better debugging experience in the mid to late 60s...
Could you elaborate on this for newbies like me?
See for example BBN Lisp manual from 1971:
http://www.softwarepreservation.org/projects/LISP/bbnlisp/Te...
See the chapter 9 on the editor, 15 on the break package, 16 on error handling, 17 on error correction, 19 on advising, 22 on the programmer's assistant, ...
In 1992 the developers of BBN Lisp / Interlisp got an ACM Software Systems award for:
> ... their pioneering work in programming environments that integrated source-language debuggers, fully compatible integrated interpreter/compiler, automatic change management, structure-based editing, logging facilities, interactive graphics, and analysis/profiling tools in the Interlisp system.
Re: Notes on debugging Clojure code
#15Earlier quoted context omitted.
This is a terrible advice, and is not even necessary. Even println debugging is better than this. Not only defs are abused contrary to their intended use, they are difficult to distinguish from legitimate defs, and thus some may (and will!) slip away into the wild.
This actually looks pretty handy. Not sure why you'd think it's so terrible - how else would you capture a request in a variable to play with it in the REPL? I think a lot of the constraints can be relaxed during the development phase.
For example a dynamic variable could be accessible in the debug repl:
CL-USER 56 > (defvar *bar*)
*BAR*
CL-USER 57 > (defun foo (baz)
(let ((*bar* (* 1000 baz)))
(break)
*bar*))
FOO
CL-USER 58 > (foo 5)
Break.
1 (continue) Return from break.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace or :c to proceed.
Type :bug-form "" for a bug report template or :? for other options.
CL-USER 59 : 1 > *bar*
5000
The value of a lexical variable can be retrieved. CL-USER 60 : 1 > :l baz
Value of BAZ is:
5
CL-USER 61 : 1 > *
5
In something like McCLIM or SLIME I would print the object to a listener and the window system remembers the value.In some other Lisp I would call (inspect foo) instead of (print foo) and the inspected values would be available in a stack of inspectors. Alternatively I would trace the functions, and the tracer would record the passed and returned objects.
Re: Notes on debugging Clojure code
#16A few more tricks: - CIDER, the de-facto Clojure environment for Emacs, has a debugger[1]. This is also true for some of the larger IDEs, like Cursive. - Timbre[2] has a number of cool debugging macros, especially spy, which you can tack onto any expression and it'll log it for you. Very useful, similar to the macro in the post, except you don't have to write it :) I really can't overestimate how valuable a real REPL…
This! Many times when people complain about Clojure, the solution is "just use already available tool X", but they say "no, I am not used to tool X" and then go to invent some half-baked solution in vanilla REPL. That is great for learning, but spreads lousy (mis)information about Clojure's development process. Folks, learn Emacs + CIDER, or at least Idea + Cursive...
Developers are very opinionated about their choice of tools.
I think our answer to critics on Clojure's debugging workflows who might be using tools that don't have first-class Clojure support shouldn't be to try to convince them to ditch their existing tools and force them to use the few tools that already have first-class support for Clojure debugging. This approach of tying the debugging experience to an editor/IDE only works when your community almost exclusively uses a few de-facto IDEs like in the Java world with Eclipse/IntelliJ and in .NET with Visual Studio. This isn't really the case with Clojure's target audience, who come from all kinds of backgrounds and prefer all kinds of different tools.
Instead, I think our answer should be to try to minimize friction for new developers by allowing them to use whatever tools they prefer and still have access to a first-class Clojure debugging experience, by decoupling debugging from editors and building a standalone debugging tool that can be run without any explicit editor support, but can of course be enhanced by editor integrations (and editors can of course still choose to maintain their own debugging mechanism). This approach would be analogous to the approach taken by the JavaScript community with various browser-based debuggers, and is why we don't hear people complain about the state of JavaScript debugging very often, even though very few editors have built-in JavaScript debugging support.
Re: Notes on debugging Clojure code
#17While I miss code-is-data very much, there is no turning back from the type system and how there are no unpure functions. It is just so mind-blowingly easy to catch almost every bug I would usually write.
I'm afraid this is the point where I should try Haskell, and be unsatisfied for the rest of my professional life.
Re: Notes on debugging Clojure code
#18A few more tricks: - CIDER, the de-facto Clojure environment for Emacs, has a debugger[1]. This is also true for some of the larger IDEs, like Cursive. - Timbre[2] has a number of cool debugging macros, especially spy, which you can tack onto any expression and it'll log it for you. Very useful, similar to the macro in the post, except you don't have to write it :) I really can't overestimate how valuable a real REPL…
In CIDER I have to mark my functions as debuggable before I can step into them - this is an odd requirement and a PITA when the functions are spread out through the codebase. It's so easy to accidentally commit a non-debuggable version as well.
Cursive is a lot better and more modern, with one painful problem: no proper support for 'break on exception'. So on exception the program just closes and I'm just left with a stacktrace in the console. You can turn on 'Java Exceptions', but this has multiple problems:
1. On load of Clojure files there is a ton of Java exceptions thrown, literally making it unusable
2. and if you disable those exceptions in the Condition field the boot up process takes forever to actually run (likely because it's interpreting the Condition field code over and over and over again..).
Any good solutions to these?
Re: Notes on debugging Clojure code
#19Re: Notes on debugging Clojure code
#20Earlier quoted context omitted.
Clojure also has better debugging experience. This is a bare-bones workflow of a guy who doesn't use the best tools available in Clojure.
Could you provide links to this better debugger