Use the template haskell (abbreviated to "TH") extension in a module with:
{-# LANGUAGE TemplateHaskell #-}
There are a few key pieces of syntax:
$(code here)
- this is called a splice. It runs thecode here
Haskell code that produces an AST, either a declaration or expression, depending on context.[|code here|]
- this is a quote. It quotes thecode here
Haskell code and produces an AST of it. You can use this to generate code automatically, or to pass code to a splice, etc. It produces aQ Exp
.'myfunc
- you can quote an individual variable name with this. It produces aName
.''mytype
- you can quote a type name with this. It produces aName
.[name|arbitrary text|]
- this is called a quasi quote. It runs the code defined inname
against thearbitrary text
string, producing an AST.
The template-haskell
package provides the AST:
Language.Haskell.TH.Syntax
- The AST starts with
Exp
. - The module
Language.Haskell.TH
provides a more high-level monadic API for generating ASTs.
Here's an example that just creates a string literal.
myString :: String
myString = $(stringE "hello!")
where
stringE :: String -> Q Exp
Here's a more complicated example:
> let foo = 1 : $(varE 'foo)
> take 10 foo
[1,1,1,1,1,1,1,1,1,1]
where
varE :: Name -> Q Exp
It quotes the name foo
and then splices that back in as a variable.
Here's a generalized variant of fst
, snd
that works on any tuple
length:
> let nth n m = lamE [tupP (map (\i -> if i == n then varP (mkName "x") else wildP) [1..m])] (varE (mkName "x"))
> $(nth 3 5) (1,2,3,4,5)
3
With -ddump-splices
:
> :set -ddump-splices
> $(nth 3 5) (1,2,3,4,5)
<interactive>:72:3-9: Splicing expression
nth 3 5 ======> \ (_, _, x, _, _) -> x
3
You can refer to variables trivially with 'foo
:
> $(varE 'map) (*2) [1..5]
<interactive>:79:3-11: Splicing expression varE 'map ======> map
[2,4,6,8,10]
If you need to make up a name, use mkName
.
> $(varE (mkName "map")) (*2) [1..5]
<interactive>:79:3-11: Splicing expression varE 'map ======> map
[2,4,6,8,10]
Use the runIO :: IO a -> Q a
function to run arbitrary I/O in a
splice.
If you read from a file, you probably want to qAddDependentFile
to
make sure that the Haskell module will be recompiled when that file
changes.
Use
reify
to get info about variables and types.
-
[Defining a quasi quoter](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell-quasi-quotation
-
Simple example package of a quasi quoter aeson-qq
Link to 'Defining a quasi quoter' is dead, click here for an archived version.