ndmitchell / debug

Haskell library for debugging
BSD 3-Clause "New" or "Revised" License
122 stars 7 forks source link

Haskell Debugger Hackage version Stackage version Linux build status Windows build status

A library for debugging Haskell programs. To use, take the functions that you are interested in debugging, e.g.:

module QuickSort(quicksort) where
import Data.List

quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = quicksort lt ++ [x] ++ quicksort gt
    where (lt, gt) = partition (<= x) xs

Turn on the TemplateHaskell, ViewPatterns and PartialTypeSignatures extensions, import Debug, indent your code and place it under a call to debug, e.g.:

{-# LANGUAGE TemplateHaskell, ViewPatterns, PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}
module QuickSort(quicksort) where
import Data.List
import Debug

debug [d|
   quicksort :: Ord a => [a] -> [a]
   quicksort [] = []
   quicksort (x:xs) = quicksort lt ++ [x] ++ quicksort gt
       where (lt, gt) = partition (<= x) xs
   |]

We can now run our debugger with:

$ ghci QuickSort.hs
GHCi, version 8.2.1: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling QuickSort        ( QuickSort.hs, interpreted )
Ok, 1 module loaded.
*QuickSort> quicksort "haskell"
"aehklls"
*QuickSort> debugView

The call to debugView starts a web browser to view the recorded information, looking something like:

Debug view output

You can look and play with the example results for various examples:

Build tool: debug-pp

debug-pp is a Haskell source preprocessor for streamlining the debug instrumentation of a module or a package. It performs the steps described above automatically. That is:

To instrument a module, add the following pragma to the top of the file:

{-# OPTIONS -F -pgmF debug-pp #-}

To instrument an entire program, add the following line to your stack descriptor, or if you don't use stack, to your cabal descriptor:

ghc-options: -F -pgmF debug-pp

In both cases you will also need to modify your Cabal descriptor in order to

Library
  ...
  build-tool-depends: debug-pp:debug-pp

Configuration

debug-pp tries to find a config file in the following locations (from higher to lower precedence):

  1. .debug-pp.yaml in the current directory (useful for per-directory settings)
  2. .debug-pp.yaml in the nearest ancestor directory (useful for per-project settings)
  3. debug-pp/config.yaml in the platform’s configuration directory (on Windows, it is %APPDATA%, elsewhere it defaults to ~/.config and can be overridden by the XDG_CONFIG_HOME environment variable; useful for user-wide settings)
  4. .debug-pp.yaml in your home directory (useful for user-wide settings)
  5. The default settings.

Use debug-pp --defaults > .debug-pp.yaml to dump a well-documented default configuration to a file, this way you can get started quickly.

The configuration options include:

Debug backends

This package offers two alternative backends for generating the debug trace:

Requirements

The function can be called multiple times with different parameters, and the results of each individual run can be selected inside the UI.

Notes

A function being debugged can refer to another function also being debugged, but due to a limitation of Template Haskell, the definition of the function being called must occur above the point of its reference in the source module.

Due to constant applicative forms (CAFs) distorting the debug trace, it is not advisable to run the debugger twice in the same GHCi session.

Limitations

This tool is quite new, so it has both limitations, places it is incomplete and bugs. Please report all the issues you find and help us make it better.

Alternatives

For practical alternatives for debugging Haskell programs you may wish to consider:

Compared to the above, debug stresses simplicity of integration and user experience.

FAQ

Q: debugView fails talking about Wine?

A: If you get wine: invalid directory "/home/f/.wine" in WINEPREFIX: not an absolute path when running debugView that means xdg-open is handled by Wine. Fix that and it will work once more.

Q: debugView fails with "error: Variable not in scope: debugView"?

A: Explicitly load the Debug module in GHCi via :m + Debug