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 hereHaskell code that produces an AST, either a declaration or expression, depending on context.[|code here|]- this is a quote. It quotes thecode hereHaskell 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 innameagainst thearbitrary textstring, producing an AST.
The template-haskell package provides the AST:
Language.Haskell.TH.Syntax- The AST starts with
Exp. - The module
Language.Haskell.THprovides 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 ExpHere'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 ExpIt 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)
3With -ddump-splices:
> :set -ddump-splices
> $(nth 3 5) (1,2,3,4,5)
<interactive>:72:3-9: Splicing expression
nth 3 5 ======> \ (_, _, x, _, _) -> x
3You 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.