Template Haskell tutorial
Template Haskell tutorial ๊ด๋ จ
- 01. The Q monad
- 02. Splicing
- 03. Limitations of TH
- 04. Quotation
- 05. Typed expressions
- 06. A few words about runQ
- 07. Names
- 08. Retrieving information about things
- 09. Example 1: instance generation
- 10. Viewing the generated code
- 11. Lifting Haskell values into TH expressions
- 12. Example 2: creating refined values at compile time
- 13. Running IO in Q
- 14. Example 3: the file-embed package
- 15. Conclusion
The tutorial aims to introduce the reader to Template Haskell (TH)โthe language extension that adds meta-programming capabilities to the Haskell language. Here I assume some familiarly with Haskell, perhaps beginner or intermediate level, although these terms are rather nebulous and subjective. To express the prerequisites in a more tangible form: if you know what a monad is, you should probably be OK.
TH has the reputation of being an expert-level topic that mere mortals are not prepared to comprehend. I donโt think this is so. The ideas behind TH are simple and make sense, while specific details can be always looked up in the Haddocks.
The tutorial cannot possibly cover every use of TH, and so it is structured in such a way so we only get to see the most common, conventional, and benign uses of this GHC feature.
Motivation
One of the main difficulties with TH is perhaps deciding whether it is the best solution to a problem at hand. Writing code that generates code is generally considered an indication that the tools of expression provided by the language and/or programmerโs imagination have failed to address a particular problem and meta-programming is used as a last resort to get things done. True or not, TH is quite popular and so knowing your way around it is a valuable skill that can be used to do things that often cannot be achieved otherwise.
Letโs list some uses of TH:
- Automatic deriving of type class instances is still perhaps the most common use case for TH. Even though the same problem can often be addressed by generics, they are known to make compilation times longer (compared to TH-based solutions), so TH is still the preferred method of automatic instance derivation in libraries like
aeson
andlens
. - Creation of TH DSLs that are integrated into systems built in Haskell. Examples of such DLSs are the language for model declaration used in persistent, and various other mini-languages used in the yesod web framework.
- Compile-time construction of values of refined types that turns invalid inputs into compilation failures.
- Compile-time loading and processing of data from external files, which is very useful sometimes. Even though this involves running
IO
during compilation, itโs a relatively innocent use case of that dangerous feature.
Reasons not to use TH:
TH helpers are often viewed as black boxes that do โmagicโ. It is not clear at all what a thing of the type Q [Dec] does, it might do anything (we will see that any code that generates declarations has the same Q [Dec] type, no matter what sort of declarations it generates). Documentation becomes the main source of information about semantics of TH code.
TH imposes restrictions on where the user should define TH functions themselves and sometimes also how to order definitions in files where TH functions are used.