Looking for FP Debuggers

(For brevity, hand waving ensues.) A personal mantra is that a language isn't worth using for 'real' development if it doesn't have a serious debugger. From what little I've read up on FP, it seems like there are fewer debuggers and fewer 'quality' ones than, say, in the more procedural camps. Might it prevent folks from seriously getting into FP? I think it does scare me away somewhat!

I'm sure there's plenty I'm not aware of so one question is: what debuggers do exist for Haskell, Clean, ML, etc.? (I have read of debuggers for Haskell, but haven't ever used them - are they truly 'commerical quality'?) O'Caml and Lisp have hum-dingers, no?

The second question I have is: do you even want to use a debugger? Or is there something magical about developing with declarative systems that means you don't, or cannot, use one?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Addendum

It would be nice if the debugger were fancy: at least interactive (as in, you can step along with the program, which doesn't appear to be the case with e.g.: Hat?), on through to perhaps insane omniscience?!

Ocaml has a time-travelling debugger...

...you can examine the program at any point during its run, and even run the program backwards.

interactivity

It would be nice if the debugger were fancy: at least interactive (as in, you can step along with the program, which doesn't appear to be the case with e.g.: Hat?)

In fact, Hat has a variety of interactive tools, but the style of interaction may be different from what you expect in the imperative/OO world. To "step along with" the computation, hat-anim shows you the reduction of a value of your choice. This reveals the non-strict order of evaluation.

However, viewing a trace is not simultaneous with the execution of the program - it is all post-mortem. So "along with" is in the semantic sense, not the practical one.

I answer: mu

My biased opinion is that if you need a debugger your development methods have failed you. I believe one should develop iteratively, and with tests, so that no problem is so large as to require recourse to a debugger.

Pffft

Pffft

Good Ideal

I agree with the goal.

Unfortunately, in actual practice, even the most thoroughly test-driven development will sometimes require the services of an actual debugger on occasion, and on those occasions you'll want a good one—particularly if good language and methodology choices have reduced the need to arising only in extremis.

Wow!

Is your company hiring? ;> Have you ever had to support someone else's code? Written 5, 10 years ago? In an "obsolete" language? ;))

Mu Two

As it happens I agree with Paul Snively's very reasonable opinion above, but since it's no fun if everyone agrees, and since your response is somewhat provocative I'm replying here.

I said "My biased opinion" when really I should have said "My provocative opinion". I was being intentionally provocative to challenge the idea that debuggers are necessary. Most of my programming these days is in PLT Scheme. I can't think of a time when I have really missed a debugger, including working on multi-threaded programs, and C interfaces. I have done some debugging using print statements and the REPL that a debugger might have made easier. However, when this has occurred it has always been a result of insufficient testing.

To address your questions, the answers are no, yes, yes, and no (though I'm not sure what obsolete means; if you have to program in it, it isn't obsolete surely? Alternatively, all mainstream languages are obsolete as they contain no more recent than 1980s programming language technology.)

To address the question I think you're asking, but haven't asked directly: I still maintain that the development methodology has failed if you find yourself spending a lot of time in the debugger. I don't care if the code was written 5-10 years ago by someone else. It may not be your fault, and you may have to take recourse to the debugger to fix up the mess, but the company has still failed to enforce good development practice. Having done the TDD thing (when I've been allowed) for almost the entire time I've been a professional programmer (no doubt a far shorter time than some of you grey beards) I'm amazed, and somewhat disgusted, that the vast majority of developers think code-and-fix is an acceptable development practice.

Don't get me wrong...

I try to use the debugger as little as possible. And I agree that in an ideal world, the time spent in the debugger should approach zero. However, the reality is that *most* programmers simply aren't interested in writing tests that they can get away with not writing, and consequently, their code contains lots of bugs that don't get discovered for many years. When someone else inherits those bugs, a debugger is about the most handy thing to have around.

I'm an unworthy tester.

Here I thought I was actually being pretty good about doing testing, but reading this thread makes me think "I suck." On the other hand, I recently had an experience in genomics where I think I had enough tests - they found the error, after all. However, the test suite could only tell me what was wrong, it could not tell me why it was wrong (new code superceded old code). So I used a debugger. I think it was more efficient than guessing at what new tests to write.

I can belive someone would say, "well, you should have written more tests as you were going along to catch this bug earlier." But I just can't believe that works in every case. Surely we can think of evil cases? And evil cases are not to be disregarded. In my case, the test would first lead one down the wrong road of thinking the new stuff was at fault. Writing ever more tests for the new stuff isn't going to help you figure out that the old stuff must be changed.

I dunno, this is all very subjective, and I would like to take to heart the point that there should be way more testing going on, but I just can't buy the mantra that with unit tests you don't need a debugger (but, I also don't buy the mantra that with unit tests you should throw out static type inference and just have dynamic typing for everything).

Please note that there is a gamut and it is all relative - I would totally repeat the comments of "if you are using a debugger then you are not doing things right to begin with" about several developers I've worked with. I'm a hypocrite!

Summary: If somebody says you never need a debugger, I think they are either not working in the kind of systems I am, or they are simply wrong. If somebody says "you should asymptotically never need a debugger because you should write better tests, but sure you might need a debugger for the weird stuff, so I would require one from my development environment, too" then I can get on board with that (but I haven't reached a sufficient plateau re: writing tests).

(I sure wish there were better tools for writing all those tests.)

QuickCheck, again...

Don't write tests, generate them!

ProtoQuickCheck.hs.

This is a very short demo of QuickCheck. QC generates test data to see if your conditions hold. QC finds evil cases quickly. Try it!

For more information, read the QuickCheck papers.

Try JVM or .NET-backend compilers

F# and SML.NET compile their code to .NET bytecode and get full MSDEV-Debugging support (I think there is a free .NET debugger included in the Framework SDK, too).

I haven't used them yet, but I guess the same should be possible for FP implementations that generate JVM bytecode.

AFAIK a main problem with debugging FP languages is that FP code usually relies heavily on tail recursion optimization which makes debugger development harder - but I never developed a debugger myself, so that might as well be nonsense. I guess lazy evaluation doesn't make things easier, either.

My personal oppinion to your second question: If you never wanted to use a debugger, you probably never developed a program with more than a few hunderd lines of code...

I've never wanted a debugger, but... tests?

I've never wanted to use a debugger, though I have tried debuggers a few times just to see what everyone was talking about. I have worked on large chunks of source.

It's my opinion that test driven development and good unit test coverage means I don't need debuggers, but I don't have any good way to measure the differences.


Whenever I've had good unit test coverage, I was able to quickly track down newfound bugs. The reason for this is that I can extend existing tests to check for the newly found problem, and immediately see which part of the code contains the error. Does anyone here have experience using debuggers together with good unit test coverage? Is it worth it?



As for Haskell debuggers, dumping the state of a running program doesn't help much when the code is executing in an unknown order. Robert Ennals wrote a fork of GHC that used speculative evaluation and include an imperative-style debugger called HsDebug. You can find more info on that in the ICFP2003 proceedings.

Uses for Debuggers

A lot depends on why you want a debugger - they're wonderful for finding out that some driver's giving you bogus capacity info, for example, and I suspect a good debugger could make tracking down space leaks in haskell code much easier by allowing the user to see the entire program graph at once.

I do think a huge amount can be done with tests and a good interpreter, though the latter becomes less useful if much of your code is monadic.

AFAIK a main problem with deb

AFAIK a main problem with debugging FP languages is that FP code usually relies heavily on tail recursion optimization which makes debugger development harder - but I never developed a debugger myself, so that might as well be nonsense.

I won't call it nonsense, but it's wrong. Tail calls don't offer any implementation difficulties for debuggers. The user-level debugging issues that tail calls create are exactly the same ones that for and while loops create -- the program state changes without changing the control context.

The real reason that FP languages don't have debuggers is mainly just that their developers and users mostly don't use them. AFAIK, Andrew Tolmach built one of the first practical time travel debuggers (ie, with unlimited history and the ability to try alternative paths) for SML/NJ, but as SML/NJ evolved it stopped working and no one really complained. It's just easier to test code interactively, using the toplevel -- and those tests can be turned directly into unit tests, too.

I respect the Ocaml team for keeping their debugger working, but I kind of wonder how many of their users actually use it. I liked having a debugger when programming in C, but have never felt the urge to use the Ocaml debugger.

Re: Java or .Net

Yup! Since I have this personal mantra of requiring a debugger, I have been looking into things like F# and Scala. Apparently, I have very bad karma (and very low tolerance) when it comes to installing systems: O'Caml, Mono, Haskell have all failed to install easily and nicely in some fashion or another (sometimes the core language is fine, but then the libraries I want are mismatched). Since I'm on a fringe system (linux ppc) I also had to go through some shenanigans to even just get Java (thanks to IBM I did get something in the end; there's also JamVM and SableVM, by the way).

So right now my favourite is Scala - I would have also tried some form of Lisp or Scheme or whatever, but I personally want static type inference. Also, I don't like the Python whitespace business.

Oh, I do have an actually relevant point to make instead of only whining! It is that I would like to be able to do functional-style programming (isn't that where you hold the gun sideways?) in Scala, but since everything goes through the Java debugger I end up in hell. The Java debugging system doesn't seem to let me do things like set a breakpoint (or step) to see what the return value of a function was. Unless the value gets assigned to a variable, and that variable is further used in the scope, when I step I end up outside the scope and have lost all references, so I can't see what the result was! It is things like that which make me so very sad. (OK, that was more whining.)

Re: we don't need no steekin' debuggers

I know I asked if people even use debuggers for FP at all, and some folks have said they do not so I should take their word that e.g.: unit tests are sufficient. However, since I haven't had such experience, I am incredulous! That may well be simply because I haven't had the experience, but I can't not wonder...

...those folks who find themselves in a world where they don't need a debugger, and only unit tests to solve all their problems must either be very smart, or not living in what most folks would call the 'real' world. (Or, FP does amazing things I didn't realize even after having used SML, Lisp, Scheme, Prolog, yadda - which could well be true!)

Now, I don't mean that those folks are on drugs or anything. What I mean is that the 'real' world has a general connotation of: people working in systems that involve multiple disparate components e.g.: industry, or, open source applications like Firefox. (I'll note that Herb Simon told us, paraphrasing, "that annoys me - university is equally the 'real' world" and I can see his point, but I think it is a point that is swimming against the grain, so to speak.) If one is interested in getting FP into the hands of the working Joe, then it is going to have to interface with more than just itself. Which means it is going to encounter real problems, and unit tests are simply not going to be the most powerful way to figure out what is happening.

And, in particular, I want to be using FP to do stuff that interfaces with other whacky things, like 2D and 3D graphics libraries, like the mouse, like random other C libraries. If I can't have a debugger for part of the layer cake, I'm pretty sure I can predict that will suck for me. (The ideal would be to be able to debug throughout the entire cake, but I realize technically at the moment that is pretty much a crack pipe dream.)

What are the key ingredients that let you not need a debugger? I think I've heard that having an interactive top level is a requisite. Is there something about Haskell vs. say ML that means Haskell doesn't need (assuming there could be a good interactive one, given all the laziness etc.) a debugger, but Lisp does? As in, Haskell is more 'pure' much more often than Lisp?

Purity helps a lot, you can r

Purity helps a lot, you can re-run anything pure as many times as you like. And monadic discipline often helps narrow down which state-like-things are relevant at a given point if any are. Rabid control over side-effects is very much your best friend when it comes to debugging.

As an aside,

My experience with SML was mostly academic, but it always seemed like once I got it properly type checking then the program was correct, right out of the gate! That could have been due to being relatively 'pure' and also due to the typechecking helping work through errors in thought / design? At any rate, I always think back on that experience with fond memories.

I need to get into Haskell more seriously, and get monads into my head, it would seem.

static typing vs debugging

It is commonly observed that once you get past a static polymorphic type checker, your program "just works". I reckon type-checking certainly catches about 90% of the bugs.

But there remain a number of thinko (as opposed to typo) errors which can still survive rigorous type-checking. I have probably written tens of thousands of lines of Haskell code, and pondered many a bug that manifests as wrong runtime output rather than a compile-time error.

As one of the few people who has extensive experience of using the Hat debugger, I can say that I personally find it massively reduces the time taken to find some of these "deep" bugs, by comparison with just staring at the source code. It is usually not a matter of writing test cases - I already have a failing case! The question is usually that the failing case involves some obscure corner behaviour on some data that has already been through 15 transformation phases from the original data entry to the apparent crash/output. It would be quite tedious to attempt to re-construct (by hand) the exact internal data structure values involved here, just to unit-test each module in isolation, with the aim of gradually whittling down the test case to a manageable size, and eventually (one hopes) to an understanding of the problem.

What the debugger gives me, is a context I can explore immediately, with the exact values seen in the failing program run. Exploration means I can follow numerous hypotheses, and get instant feedback on whether the hypothesis is leading me somewhere useful.

To paraphrase Eric Raymond:
"With a decent tracer, all bugs are shallow."

Re: shallow bugs a tracer makes

I think that's pretty much the kind of thing I'd been hoping to hear when I initially posted - it really jives with my own experience. (Of course that doesn't mean I don't like or learn from the other comments!)

Having said that, I think it is mostly for situations where you are entirely inside the FP language; there's still a problem when somebody wants to use FP with some other tools; how do you trace what happened when Haskell made an FFI call to your OpenGL system?

Visualize that metaphor :)

I have to admit that "a point that is swimming against the grain" somehow tickled my funny bone as I tried to visualize it. Thanks=)