Thursday, April 17, 2008

Niffy Template Haskell trick for better trace

This trick is by EvilTerran from #haskell freenode IRC channel.

A quick and dirty way to debug Haskell code is to use trace function from Debug.Trace module.

For example,

f (trace ("arg1: " ++ show arg1) arg1) arg2

would print

arg1: <actual value of arg1>

Usung Template Haskell, above code can be shortened.

> {-# LANGUAGE TemplateHaskell #-}

This means this code uses TemplateHaskell extension.

> module Trace where

Let's call the module Trace.

> import Language.Haskell.TH
> import Debug.Trace (trace)

And, import some modules.

> t name = [| trace ($(litE . StringL $ nameBase name)
>    ++ ": " ++ show $(varE name)) $(varE name) |]

Then, define the macro t that can be called as:

$(t 'varName)

And it'll be expanded to:

trace ("varName" ++ ": " ++ show varName) varName

Let's actually use Trace.t in Main module.

> {-# LANGUAGE TemplateHaskell #-}

Main module also needs to use TemplateHaskell extension because it'll include macro call like $(t 'a).

> module Main where
> import Trace (t)
> import Debug.Trace (trace)

> main = do
>     let a = 2
>     let bravo = 40
>     putStrLn $ show ((trace ("a: " ++ show a) a) + bravo)

compare this with

>     putStrLn $ show ($(t 'bravo) + $(t 'a))

>     return ()

Shorter.

Now, I need vim keyboard shortcut that'll replace the word under cursor with $(t 'wordUnderCuror). And, another keyboard shortcut that'll turn $(t 'word) into word.

Edit: Alok commented with vim function below:

" word <===> $(t 'word)
" by Alok
function! ToggleTrace()
    call searchpos('\|)')
    let b = searchpos('\<', 'b')
    let s = searchpos("\$(t '", 'b')
    if s[0] == b[0] && b[1] - s[1] == len("$(t '")
        " remove trace
        norm df'f)x
    else
        " add trace
        call insert (b, 0, 0)
        call add (b, 0)
        call setpos('.', b)
        let @z = "$(t '"
        norm "zP
        let @z = ")"
        norm f "zP
    endif
endfunction

nmap <leader>t :call ToggleTrace()<cr>

Thank you Alok.