When debugging Haskell code, it can be useful to look at the code generated from Template Haskell and Quasiquote expressions. This repository is a tutorial explaining an easy way to view the generated Haskell code.
This repository contains a single Haskell file, Example.hs. It is a small example of using heterocephalus, a type-safe template engine.
The Haskell code defining the template looks like this:
template :: Html
template =
let a = "hello"
b = 3 :: Int
cs = ["foo", "bar", "baz"]
in [compileText|
variable interpolation:
#{a}
if control statement:
%{ if (b == 3) }
b is 3
%{ else }
b is some other number
%{ endif }
forall control statement:
%{ forall c <- cs }
#{c}
%{ endforall }
|]
Everything within the compileText
quasiquote is expanded at compile-time into
Haskell code. This tutorial explains how to see exactly what code is
generated.
First, clone this repository and cd
into it.
$ git clone https://github.com/cdepillabout/heterocephalus-example.git
$ cd heterocephalus-example/
This tutorial assumes you are using stack
. You need to install
stack
if you
haven't already done so.
If GHC is not installed, it can be installed with stack setup
.
$ stack setup
Now that GHC is installed, the example program can be built with stack build
.
$ stack build
The example program is built and placed somewhere under the .stack-work/
directory. stack exec
can be used to execute it:
$ stack exec -- heterocephalus-example
variable interpolation:
hello
if control statement:
b is 3
forall control statement:
foo
bar
baz
The example program needs to be compiled with the GHC flag -ddump-splices
in
order to get GHC/Stack to dump the generated Haskell code to a file.
If the example program has already been built (as above), then stack clean
needs to be run to make sure the executable gets rebuilt. After that, stack build
can be run again, but this time with the -ddump-splices
flag.
$ stack clean
$ stack build --ghc-options="-ddump-splices"
NOTE: Not running stack clean
first can cause stack build
to appear to
succeed, but no splice file to be generated. Make sure stack clean
is run
before stack build
whenever -ddump-splices
is being used.
GHC/Stack will generate a splice file somewhere under .stack-work/
. The
location of the splice file will change depending on the architecture and Cabal
version. find
can be used to figure out where the splice file is.
$ find .stack-work/ -name '*.dump-splices'
.stack-work/dist/x86_64-linux/Cabal-1.24.0.0/build/heterocephalus-example/heterocephalus-example-tmp/app/Example.dump-splices
This .dump-splices
file will show what Haskell code is generated from each
Template Haskell and quasiquote expression.
For example, a quasiquote like this:
[compileText|foo #{a}|]
would produce Haskell code that looks like this (slightly simplified):
do
preEscapedText "foo "
preEscapedToMarkup a
Lets go back to the example program. Once again, the template is defined like this:
template :: Html
template =
let a = "hello"
b = 3 :: Int
cs = ["foo", "bar", "baz"]
in [compileText|
variable interpolation:
#{a}
if control statement:
%{ if (b == 3) }
b is 3
%{ else }
b is some other number
%{ endif }
forall control statement:
%{ forall c <- cs }
#{c}
%{ endforall }
|]
If this is compiled with the -ddump-splices
flag, the output
Example.dump-splices
file will look like this (after being cleaned up a
little to make it more readable):
app/Example.hs:(15,19)-(30,5): Splicing expression
template-haskell-2.11.0.0:Language.Haskell.TH.Quote.quoteExp
compileText
"variable interpolation:
#{a}
if control statement:
%{ if (b == 3) }
b is 3
%{ else }
b is some other number
%{ endif }
forall control statement:
%{ forall c <- cs }
#{c}
%{ endforall }
"
======>
do
preEscapedText "variable interpolation:\n"
preEscapedToMarkup a
preEscapedText "\n\nif control statement:\n"
condH
[(b == 3, preEscapedText " b is 3")]
(Just (preEscapedText " b is some other number"))
preEscapedText "\n\nforall control statement:\n"
forM_ cs $ \c -> do
preEscapedText " "
preEscapedToMarkup c
preEscapedText "\n"
Above the =======>
line, you can see the quasiquote as it exists in the
Haskell file. Below the line, you can see the Haskell code that GHC has
generated.
Sometimes it is difficult to read the splice files, but it there is often no other way to easily debug Template Haskell and quasiquotes.