Debugging
There are several techniques we can use to debug Haskell programs. Below are the techniques I use, in the order I usually use them :).
1 Types
In Haskell, if the program typechecks, then there are probably not too many bugs. Here are some practices I follow, to get my code to typecheck:
You can start with the most specific type first, then write your function. If everything works and / or you need a more general type, delete the type signature and let your IDE suggest a more general one.
- Work out the types of new programs in your head or on paper before writing code.
- When writing a function, write the type signature first.
- Compile your code often, to perform typechecking. Pay attention to any type errors, as soon as you observe them.
- If you encounter a type error, this page has good recommendations for debugging it. There is a lot of information on that page. I recommend starting with Cutting Through the Noise, which describes how to read a type error.
2 Tests
- Write a run a test suite, to identify where there might be errors.
- Note that it is difficult for a failing test to diagnose an error. Instead, it indicates the presence of an error. To diagnose it, try using the next technique to find the smallest example that still triggers the bug.
3 Evaluate example expressions
- Use
stack ghcito start an interactive Haskell session, with your code loaded into it. - Evaluate small expressions that trigger the bug, to gain insight into the bug.
4 Trace by hand
If the bug persists, trace through the small expressions “by hand”, by looking through the code and following the computation in your head.
5 Use Debug.Trace
If the bug persists, use Haskell’s Debug.Trace to print the results of intermediate expressions.
To use Debug.Trace:
- Import the library.
- Use the
tracefunction to print intermediate results.
See this page for more information and examples. There are also other tracing / observation libraries available.
6 Step through in a debugger
If all else fails, we can use a debugger. Because Haskell is lazily evaluated, a step-through debugger can be less useful in Haskell than in other, strict languages. Setting up the debugger also takes some work. That’s why I choose this option as a last resort.
Setting up the debugger
To set up the debugger in VSCode:
You should need to do this only once, the first time you set up the debugger.
- Run the following command:
stack update
stack install haskell-dap ghci-dap haskell-debug-adapterInstall the VSCode extension for the Haskell debugger.
Configure your debugger to (sort-of) show the values of intermediate expressions. Open the file
.vscode/launch.jsonand make sure thehaskell(stack)configuration includes the setting
"forceInspect": trueUsing the debugger
See the VSCode extension page for screenshots and examples.
- Set breakpoints in your code.
- Launch the debugger using VSCode’s interface.