I'm interested in what tools and methods are used for diagnosing flaws in large scale functional programs. What tools are useful? My current understanding is that 'printf' debugging (e.g. add logging and redeploy) is what is typically used.
If you've done debugging of a functional system what was different about it then debugging a system built with an OO or procedural language?
Sadly, printf
debugging seems to be the state of practice for Standard ML, Objective Caml, and Haskell. There's a little bit of debugging at the interactive read-eval-print loop, but once your app hits 25,000 or 50,000 lines that's less useful.
If you're lucky enough to be using Haskell, there's an exception: QuickCheck is indispensible for testing and deubgging. QuickCheck can be used even on combinations of Haskell and C code, as demonstrated by experience with the Xmonad window manager.
It's worth noting that around 1990 Andrew Tolmach built a very nice time-travel debugger for Standard ML of New Jersey, but that it was not considered worth maintaining. It's also worth noting that at one point the OCaml debugger (also a time-travel debugger) worked only on bytecode, which was inconvneient, and refused to violate abstraction barriers, which made it useless. This was around release 3.07 or so; perhaps things have improved.
Also in the early 1990s, Henrik Nilsson built an interesting debugger for Haskell, but mostly what it did was prevent the debugger from accidentally changing the evaluation behavior of the program. This was interesting, but only to lavzy-evaluation weenies.
As someone who has built or worked on large applications in all three of these languages, I find the state of play depressing.
The main tools we use at work (a Haskell shop) are:
- QuickCheck
- HPC: visual Haskell program coverage tool (we developed this in house)
- Logging/printf/trace
- Sometimes, the GHCi debugger
My current job is to implement new features and support a large system implemented in ocaml and C#. Most of the "logic" is implemented in caml and the GUI and data access is in C#. The debugging techniques are pretty much as you describe lots of logging and assert to work out what's gone wrong.
Additionally we have a large number of unit tests, which are just caml scripts for testing the logic and help to spot any regression errors.
We also use continuous integration to check the build and run nightly test scripts, including some automated testing of the GUI though our "automation" style scripting interface.
I quite often use the C# debugger for debugging the C# portion of the application, the ocaml debugger does yet work under windows so we don't use it. Although we hope one day we may fix this but it isn't top of our priority list. I have occasionally used windbg to investigate managed and unmanaged memory problems, though this turned out to be caused by a third party component implemented in C#.
So overall, nothing out of the ordinary but it seems to work okay, we don't see too many production problems.
Thanks,
Rob
F# has Visual Studio integration, so you can attach the debugger to your program and set breakpoints, watches, etc, just like with any other .NET language.
However, I prefer to avoid debugging as much as possible, by writing short functions that I can unit-test individually.
A couple of years ago when I did this I had to use a combination of printf debugging and QuickCheck. These days I would also use the ghci built-in debugger.
The biggest headache was actually laziness causing space-time leaks. There still doesn't seem to be a good answer to these: just do lots of profiling and keep trying to figure it out.
OCaml and F# both have excellent debuggers. OCaml's is time reversible. F#'s has excellent IDE and multithreading support.