Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Up to now I have always worked with imperative languages like Pascal, C, C++, Java in a production environment, so I have experience with debuggers for these languages (Turbo Pascal, Turbo C, GDB / DDD, Visual Studio, Eclipse).

A debugger for an imperative language normally allows to

  • Set break points on statements, and execute the program until the next break point is encountered.
  • Execute the program one statement at a time, with the option of entering a function / method call or skipping over to the following statement.

When program execution is paused, one can examine the current state of the program, i.e.:

  • Inspect the contents of the stack to see the current nesting of function calls.
  • Inspect local variables, actual function parameters, global variables, and so on.

Since I have never worked with a functional programming language in a production environment, I was trying to figure out how a debugger for such a language would look like. For example:

  • How does the debugger walk through the program if there are no statements like in an imperative language? Where does one set break points?
  • In a language with lazy evaluation like Haskell, the sequence of function calls can be different from that of an eager language: will this make a big difference when trying to understand the call stack?
  • What about variables? Since there is no state, I imagine that a debugger will only show the variable bindings for the current scope (?), i.e. there will be no local or global variables changing value as I step through the program.

Summarizing, are there any general, common features of functional-language debuggers that clearly distinguish them from imperative-language debuggers?

share|improve this question

2 Answers 2

up vote 3 down vote accepted

Deep under the hood, any functional language with heavy roots in lambda calculus is compiled to code, closely following some abstract computation model. For haskell it is STG-machine, for ocaml/caml-light it was CAM-machine. This execution model usually has something like local variables and clear route of execution and source code translates into code for abstract machine by clear, well-defined algorithm. So, breakpoints can be inserted in haskell (for example) code and something like local variables can be determined, when expression with breakpoint is evaluated.

For haskell and other lazy languages, however, the problem is that it does not make much good (except profiling). REPL allows coding expressions with easy, as no side-effects goes away from function body definition, so no real need to debug functions locally and asserts will locate error for sure. The real source of bugs usually is space leak due laziness in wrong place, and completely different tools, inspecting heap structure, are needed (and some are available).

share|improve this answer

How does the debugger walk through the program if there are no statements like in an imperative language? Where does one set break points?

Most functional languages still have statements; they work as usual. Otherwise, it's generally on entrance to a function call or occasionally on a sub-expression.

In a language with lazy evaluation like Haskell, the sequence of function calls can be different from that of an eager language: will this make a big difference when trying to understand the call stack?

I've not done a lot, but from what I've seen it doesn't make a big difference in practice. Even in the worst case, it behaves similarly to concurrent imperative programs where the call stack itself is largely useless in determining the source of the bug.

What about variables? Since there is no state, I imagine that a debugger will only show the variable bindings for the current scope (?), i.e. there will be no local or global variables changing value as I step through the program.

Again, most functional languages have at least some state. The bigger impact is more along the lines of evaluating a lazy construct causing your program to be weird. This though isn't much different from lazy constructs in imperative languages like Python generators or C# enumerables.


In the end, there's not a whole lot of difference in my experience.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.