Difference between revisions of "Template Haskell"

From HaskellWiki
Jump to navigation Jump to search
m
 
(62 intermediate revisions by 23 users not shown)
Line 1: Line 1:
'''[http://www.haskell.org/th/ Template Haskell]''' is a [[GHC]] extension to Haskell that adds compile-time metaprogramming facilities. The original design can be found here: http://research.microsoft.com/~simonpj/papers/meta-haskell/ . It is [http://haskell.cs.yale.edu/ghc/docs/6.2/html/users_guide/template-haskell.html included] in GHC version 6.
+
'''[http://hackage.haskell.org/package/template-haskell Template Haskell]''' is a [[GHC]] extension to Haskell that adds compile-time metaprogramming facilities. The original design can be found [https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.189.4479&rep=rep1&type=pdf here]. It is [https://downloads.haskell.org/ghc/8.10.3/docs/html/users_guide/glasgow_exts.html#template-haskell included] in GHC since version 6.
  +
  +
{{GHCUsersGuide|exts/template_haskell||a Template Haskell section}}
   
 
This page hopes to be a more central and organized repository of TH related things.
 
This page hopes to be a more central and organized repository of TH related things.
  +
   
 
=What is Template Haskell?=
 
=What is Template Haskell?=
  +
 
Template Haskell is an extension to Haskell 98 that allows you to do type-safe compile-time meta-programming, with Haskell both as the manipulating language and the language being manipulated.
 
Template Haskell is an extension to Haskell 98 that allows you to do type-safe compile-time meta-programming, with Haskell both as the manipulating language and the language being manipulated.
   
 
Intuitively Template Haskell provides new language features that allow us to convert back and forth between concrete syntax, i.e. what you would type when you write normal Haskell code, and abstract syntax trees. These abstract syntax trees are represented using Haskell datatypes and, at compile time, they can be manipulated by Haskell code. This allows you to reify (convert from concrete syntax to an abstract syntax tree) some code, transform it and splice it back in (convert back again), or even to produce completely new code and splice that in, while the compiler is compiling your module.
 
Intuitively Template Haskell provides new language features that allow us to convert back and forth between concrete syntax, i.e. what you would type when you write normal Haskell code, and abstract syntax trees. These abstract syntax trees are represented using Haskell datatypes and, at compile time, they can be manipulated by Haskell code. This allows you to reify (convert from concrete syntax to an abstract syntax tree) some code, transform it and splice it back in (convert back again), or even to produce completely new code and splice that in, while the compiler is compiling your module.
   
For email about Template Haskell, use the [http://haskell.org/mailman/listinfo/glasgow-haskell-users GHC users mailing list]. It's worth joining if you start to use TH.
+
For email about Template Haskell, use the [http://www.haskell.org/mailman/listinfo/glasgow-haskell-users GHC users mailing list]. It's worth joining if you start to use TH.
  +
   
 
= Template Haskell specification =
 
= Template Haskell specification =
Line 14: Line 19:
 
Template Haskell is only documented rather informally at the moment. Here are the main resources:
 
Template Haskell is only documented rather informally at the moment. Here are the main resources:
   
* [http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html The user manual section on Template Haskell]
+
* [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/exts/template_haskell.html The user manual section on Template Haskell]
* [http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html#th-quasiquotation The user manual section on quasi-quotation], which is closely related to Template Haskell.
+
* [https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/exts/template_haskell.html#template-haskell-quasi-quotation The user manual section on quasi-quotation], which is closely related to Template Haskell.
* [http://research.microsoft.com/~simonpj/papers/meta-haskell/ The original Template Haskell paper]
+
* [https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/meta-haskell.pdf The original Template Haskell paper]
* [http://research.microsoft.com/~simonpj/tmp/notes2.ps Notes on Template Haskell version 2], which describes changes since the original paper. Section 8 describes the difficulty with pattern splices, which are therefore not implemented.
+
* [https://www.haskell.org/ghc/docs/papers/th2.ps Notes on Template Haskell version 2], which describes changes since the original paper. Section 8 describes the difficulty with pattern splices, which are therefore not implemented.
* [http://www.haskell.org/ghc/docs/latest/html/libraries/template-haskell/Language-Haskell-TH.html The Template Haskell API]
+
* [http://hackage.haskell.org/package/template-haskell The Template Haskell API]
   
 
= Template Haskell tutorials and papers =
 
= Template Haskell tutorials and papers =
   
 
* Bulat's tutorials:
 
* Bulat's tutorials:
  +
*# [http://web.archive.org/web/20100703060856/http://www.haskell.org/bz/thdoc.htm Wayback Machine], [http://docs.google.com/uc?id=0B4BgTwf_ng_TM2MxZjJjZjctMTQ0OS00YzcwLWE5N2QtMDI0YzE4NGUwZDM3 Google Docs]
** http://www.haskell.org/bz/thdoc.htm
 
  +
*# [http://web.archive.org/web/20100703060841/http://www.haskell.org/bz/th3.htm Wayback Machine], [http://docs.google.com/uc?id=0B4BgTwf_ng_TOGJkZjM4ZTUtNGY5My00ZThhLTllNDQtYzJjMWJiMzJhZjNj Google Docs]
** http://www.haskell.org/bz/th3.htm
 
  +
 
: One reader said "These docs are *brilliant* ! Exactly what I need to get an understanding of TH."
 
: One reader said "These docs are *brilliant* ! Exactly what I need to get an understanding of TH."
   
  +
<small>(Note: These documents are from [http://www.archive.org the Wayback machine] because the originals disappeared. They're public documents on Google docs, which shouldn't require logging in. However, if you're asked to sign in to view them, you're running into a known Google bug. You can fix it by browsing to [http://www.google.com Google], presumably gaining a cookie in the process.)</small>
  +
  +
* Dominik's [[A practical Template Haskell Tutorial| example-driven, practical introduction to Template Haskell]].
  +
  +
<!--
 
* Mark Snyder's Template Haskell chapter on the Software Generation and Configuration Report
 
* Mark Snyder's Template Haskell chapter on the Software Generation and Configuration Report
 
** http://nix.cs.uu.nl/dist/courses/sgc-report-unstable-latest/manual/chunk-chapter/templatehaskell.html
 
** http://nix.cs.uu.nl/dist/courses/sgc-report-unstable-latest/manual/chunk-chapter/templatehaskell.html
  +
-->
 
 
* A very short tutorial to understand the basics in 10 Minutes.
 
* A very short tutorial to understand the basics in 10 Minutes.
** http://www.hyperedsoftware.com/blog/entries/first-stab-th.html
+
** https://web.archive.org/web/20170331032455/www.hyperedsoftware.com/blog/entries/first-stab-th.html
  +
  +
* GHC Template Haskell documentation
  +
** https://downloads.haskell.org/ghc/latest/docs/html/users_guide/exts/template_haskell.html
   
 
* Papers about Template Haskell
 
* Papers about Template Haskell
  +
** Template metaprogramming for Haskell, by Tim Sheard and Simon Peyton Jones, May 2002. [http://haskell.org/th/papers/meta-haskell.ps [A4 ps]] [http://haskell.org/th/papers/meta-haskell.bib [bib]]
 
** Template Haskell: A Report From The Field, by Ian Lynagh, May 2003.[http://haskell.org/th/papers/Template_Haskell-A_Report_From_The_Field.ps [A4 ps]] [http://haskell.org/th/papers/Template_Haskell-A_Report_From_The_Field.bib [bib]]
+
** Template metaprogramming for Haskell, by Tim Sheard and Simon Peyton Jones, Oct 2002. [[http://www.haskell.org/wikiupload/c/ca/Meta-haskell.ps ps]]
** Unrolling and Simplifying Expressions with Template Haskell, by Ian Lynagh, May 2003.[http://haskell.org/th/papers/Unrolling_and_Simplifying_Expressions_with_Template_Haskell.ps [A4 ps]][http://haskell.org/th/papers/Unrolling_and_Simplifying_Expressions_with_Template_Haskell.bib [bib]]
+
** Template Haskell: A Report From The Field, by Ian Lynagh, May 2003. [[http://www.haskell.org/wikiupload/2/24/Template_Haskell-A_Report_From_The_Field.ps ps]]
** Automatic skeletons in Template Haskell, by Kevin Hammond, Jost Berthold and Rita Loogen, June 2003. [http://haskell.org/th/papers/hlpp.ps [A4 ps]][http://haskell.org/th/papers/hlpp.bib [bib]]
+
** Unrolling and Simplifying Expressions with Template Haskell, by Ian Lynagh, December 2002. [[http://www.haskell.org/wikiupload/e/ed/Template-Haskell-Utils.ps ps]]
** Optimising Embedded DSLs using Template Haskell, by Sean Seefried, Manuel Chakravarty, Gabriele Keller, March 2004. [http://haskell.org/th/papers/th-pan.ps [A4 ps]] [http://haskell.org/th/papers/th-pan.bib [bib]]
+
** Automatic skeletons in Template Haskell, by Kevin Hammond, Jost Berthold and Rita Loogen, June 2003. [[http://www.haskell.org/wikiupload/6/69/AutoSkelPPL03.pdf pdf]]
** Typing Template Haskell: Soft Types, by Ian Lynagh, August 2004.[http://haskell.org/th/papers/Typing_Template_Haskell__Soft_Types.ps [A4 ps]][http://haskell.org/th/papers/Typing_Template_Haskell__Soft_Types.bib [bib]]
+
** Optimising Embedded DSLs using Template Haskell, by Sean Seefried, Manuel Chakravarty, Gabriele Keller, March 2004. [[http://www.haskell.org/wikiupload/b/b5/Seefried04th-pan.pdf pdf]]
  +
** Typing Template Haskell: Soft Types, by Ian Lynagh, August 2004. [[http://www.haskell.org/wikiupload/7/72/Typing_Template_Haskell_Soft_Types.ps ps]]
   
 
= Other useful resources =
 
= Other useful resources =
   
  +
* (2011) [https://github.com/leonidas/codeblog/blob/master/2011/2011-12-27-template-haskell.md Basic Tutorial of Template Haskell]
* [http://www.haskell.org/th/ The old Template Haskell web page]. Would someone feel like moving this material into the HaskellWiki?
 
  +
* Old and probably not too useful for most but maybe... http://www.cse.unsw.edu.au/~chak/haskell/ghc/comm/exts/th.html
 
  +
* (2011) Greg Weber's [http://www.yesodweb.com/blog/2011/10/code-generation-conversation blog post on Template Haskell and quasi-quoting] in the context of Yesod.
*[http://web.comlab.ox.ac.uk/oucl/work/ian.lynagh/Fraskell/ Fraskell documentation] & explanation of how Template Haskell is used to vastly speed it up.
 
  +
Feel free to update our Wikipedia entry
 
  +
* (2012) Mike Ledger's [http://quasimal.com/posts/2012-05-25-quasitext-and-quasiquoting.html tutorial on TemplateHaskell and QuasiQuotation] for making an interpolated text QuasiQuoter. Here's another [http://www.well-typed.com/blog/2014/10/quasi-quoting-dsls/ great 2014 blog post on quasiquotation].
http://en.wikipedia.org/wiki/Template_Haskell
 
  +
  +
<!-- * [http://www.haskell.org/th/ The old Template Haskell web page]. Would someone feel like moving this material into the HaskellWiki?
  +
-->
  +
<!-- * Old and probably not too useful for most but maybe... http://www.cse.unsw.edu.au/~chak/haskell/ghc/comm/exts/th.html
  +
-->
  +
* [https://web.archive.org/web/20120208093803/http://www.cs.ox.ac.uk/people/ian.lynagh/Fraskell/ Fraskell documentation] & explanation of how Template Haskell is used to vastly speed it up.
  +
  +
* [[Quasiquotation]]
  +
  +
Feel free to update our Wikipedia entry http://en.wikipedia.org/wiki/Template_Haskell
   
 
= Projects =
 
= Projects =
Line 53: Line 78:
 
What are you doing/planning to do/have done with Template Haskell?
 
What are you doing/planning to do/have done with Template Haskell?
   
* The [http://www.ict.kth.se/org/ict/ecs/sam/projects/forsyde/www ForSyDe methodology] is currently implemented as a Haskell-based DSL which makes extensive use of Template Haskell.
+
* The [https://forsyde.github.io/ ForSyDe methodology] is currently implemented as a Haskell-based DSL which makes extensive use of Template Haskell.
   
* I have written a primitive (untyped) binding to the Objective-C runtime system on Mac OS X. It needs just TH, no "stub files" are created, no seperate utilities are required. Initial snapshot is at http://www.kfunigraz.ac.at/imawww/thaller/wolfgang/HOC020103.tar.bz2 -- WolfgangThaller
+
* I have written a primitive (untyped) binding to the Objective-C runtime system on Mac OS X. It needs just TH, no "stub files" are created, no separate utilities are required. Initial snapshot is at http://www.kfunigraz.ac.at/imawww/thaller/wolfgang/HOC020103.tar.bz2 -- WolfgangThaller
   
 
* I am writing Template Greencard - a reimplementation of GreenCard using TH. Many bits work out really nicely. A few bits didn't work so nicely - once I get some time to think, I'll try to persuade the TH folk to make some changes to fix some of these. -- AlastairReid
 
* I am writing Template Greencard - a reimplementation of GreenCard using TH. Many bits work out really nicely. A few bits didn't work so nicely - once I get some time to think, I'll try to persuade the TH folk to make some changes to fix some of these. -- AlastairReid
   
* I'm writing Hacanon - a framework for automatic generation of C++ bindings. Read "automated Template Greencard for C++" (-: Darcs repo: http://www.ScannedInAvian.org/repos/hacanon - You'll need gccxml (http://www.gccxml.org/) to compile the exmples. - 27 Dec Lemmih.
+
* I'm writing Hacanon - a framework for automatic generation of C++ bindings. Read "automated Template Greencard for C++" (-: Darcs repo: <nowiki>http://www.ScannedInAvian.org/repos/hacanon</nowiki> - You'll need gccxml (<nowiki>http://www.gccxml.org/</nowiki>) to compile the examples. - 27 Dec Lemmih.
   
 
* Following other FFI tools developers, I see some future for Template HSFFIG, especially when it comes to autogenerate peek and poke methods for structures defined in C; may be useful for implementation of certain network protocols such as X11 where layout of messages is provided as C structure/union declaration. - 16 Dec 2005 DimitryGolubovsky
 
* Following other FFI tools developers, I see some future for Template HSFFIG, especially when it comes to autogenerate peek and poke methods for structures defined in C; may be useful for implementation of certain network protocols such as X11 where layout of messages is provided as C structure/union declaration. - 16 Dec 2005 DimitryGolubovsky
   
* I am using Template Haskell as a mechanism to get parsed, typechecked code into an Ajax based Haskell Equational Reasoning tool [[Haskell Equational Reasoning Assistant]], as well as simplify the specification of equational relationships between pieces of code. There was a quicktime movie of the tool being used on http://www.gill-warbington.com/home/andy/share/hera1.html - AndyGill
+
* I am using Template Haskell as a mechanism to get parsed, typechecked code into an Ajax based Haskell Equational Reasoning tool [[Haskell Equational Reasoning Assistant]], as well as simplify the specification of equational relationships between pieces of code. There was a quicktime movie of the tool being used on <nowiki>http://www.gill-warbington.com/home/andy/share/hera1.html</nowiki> - AndyGill
   
 
* I am working on functional metaprogramming techniques to enhance programming reliability and productivity, by reusing much of the existing compiler technology. Template Haskell is especially interesting for me because it permits to check size information of structures by the compiler, provided this information is available at compile time. This approach is especially appropriate for hardware designs, where the structures are fixed before the circuit starts operating. See our metaprogramming web page at http://www.infosun.fmi.uni-passau.de/cl/metaprog/ -- ChristophHerrmann(http://www.cs.st-and.ac.uk/~ch)
 
* I am working on functional metaprogramming techniques to enhance programming reliability and productivity, by reusing much of the existing compiler technology. Template Haskell is especially interesting for me because it permits to check size information of structures by the compiler, provided this information is available at compile time. This approach is especially appropriate for hardware designs, where the structures are fixed before the circuit starts operating. See our metaprogramming web page at http://www.infosun.fmi.uni-passau.de/cl/metaprog/ -- ChristophHerrmann(http://www.cs.st-and.ac.uk/~ch)
Line 77: Line 102:
 
= Known Bugs =
 
= Known Bugs =
   
Take a look at the [http://cvs.haskell.org/trac/ghc/query?status=new&status=assigned&status=reopened&component=Template+Haskell&order=priority open bugs against Template Haskell] on the GHC bug tracker.
+
Take a look at the [http://hackage.haskell.org/trac/ghc/query?status=new&status=assigned&status=reopened&component=Template+Haskell&order=priority open bugs against Template Haskell] on the GHC bug tracker.
   
 
= Wish list =
 
= Wish list =
   
Things that Ian Lynagh (Igloo) mentioned in his paper ''Template Haskell: A Report From The Field'' in May 2003 (available [http://www.haskell.org/th/papers.html here]), by section:
+
Things that Ian Lynagh (Igloo) mentioned in his paper ''Template Haskell: A Report From The Field'' in May 2003 (available [http://www.haskell.org/wikiupload/2/24/Template_Haskell-A_Report_From_The_Field.ps here]), by section:
   
 
* Section 2 (curses)
 
* Section 2 (curses)
Line 90: Line 115:
 
* Section 3 (deriving instances of classes)
 
* Section 3 (deriving instances of classes)
 
** <strike>First-class reification</strike> (the <hask>reify</hask> function)
 
** <strike>First-class reification</strike> (the <hask>reify</hask> function)
** A way to discover whether a data constructor was defined infix or prefix (which is necessary to derive instances for <hask>Read</hask> and <hask>Show</hask> as outlined in [http://www.haskell.org/onlinereport/derived.html The Haskell 98 Report: Specification of Derived Instances]) (if there is a way, [http://www-users.cs.york.ac.uk/~ndm/derive/ Derive] seems ignorant of it)
+
** A way to discover whether a data constructor was defined infix or prefix (which is necessary to derive instances for <hask>Read</hask> and <hask>Show</hask> as outlined in [http://www.haskell.org/onlinereport/derived.html The Haskell 98 Report: Specification of Derived Instances]) (if there is a way, [http://community.haskell.org/~ndm/derive/ Derive] seems ignorant of it)
 
** Type/context splicing (in <hask>instance</hask> headers in particular)
 
** Type/context splicing (in <hask>instance</hask> headers in particular)
   
Line 100: Line 125:
   
 
* Section 6 (pan)
 
* Section 6 (pan)
** Type info again, and strictnes info too (this one seems a bit pie-in-the-sky...)
+
** Type info again, and strictness info too (this one seems a bit pie-in-the-sky...)
   
 
(Please leave the implemented ones here, but crossed off.)
 
(Please leave the implemented ones here, but crossed off.)
Line 107: Line 132:
   
 
* A TH tutorial (mainly a distillation and update of ''Template Meta-programming in Haskell'' at this point)
 
* A TH tutorial (mainly a distillation and update of ''Template Meta-programming in Haskell'' at this point)
* Write Haddock documentation for the Template Haskell library (http://hackage.haskell.org/trac/ghc/ticket/1576).
+
* <strike>Write Haddock documentation for the Template Haskell library (http://hackage.haskell.org/trac/ghc/ticket/1576).</strike>
 
* Make `reify` on a class return a list of the instances of that class (http://www.haskell.org/pipermail/template-haskell/2005-December/000503.html). (See also [http://hackage.haskell.org/trac/ghc/ticket/1577 GHC ticket #1577].)
 
* Make `reify` on a class return a list of the instances of that class (http://www.haskell.org/pipermail/template-haskell/2005-December/000503.html). (See also [http://hackage.haskell.org/trac/ghc/ticket/1577 GHC ticket #1577].)
 
* A set of simple examples on this wiki page
 
* A set of simple examples on this wiki page
Line 149: Line 174:
 
[InstanceD [] (AppT (AppT (ConT MMixMemory.SignConversion) (ConT GHC.Base.Int)) (ConT GHC.Word.Word)) []]
 
[InstanceD [] (AppT (AppT (ConT MMixMemory.SignConversion) (ConT GHC.Base.Int)) (ConT GHC.Word.Word)) []]
 
</haskell>
 
</haskell>
  +
  +
== What can <tt>reify</tt> see? ==
  +
  +
When you use <tt>reify</tt> to give you information about a <tt>Name</tt>, GHC will tell you what it knows. But sometimes it doesn't know stuff. In particular
  +
  +
* '''Imported things'''. When you reify an imported function, type constructor, class, etc, from (say) module M, GHC runs off to the interface file <tt>M.hi</tt> in which it deposited all the info it learned when compiling M. However, if you compiled M without optimisation (ie <tt>-O0</tt>, the default), and without <tt>-XTemplateHaskell</tt>, GHC tries to put as little info in the interface file as possible. (This is a possibly-misguided attempt to keep interface files small.) In particular, it may dump only the name and kind of a data type into <tt>M.hi</tt>, but not its constructors.
  +
: Under these circumstances you may reify a data type but get back no information about its data constructors or fields. Solution: compile M with
  +
:* <tt>-O</tt>, or
  +
:* <tt>-fno-omit-interface-pragmas</tt> (implied by -O), or
  +
:* <tt>-XTemplateHaskell</tt>.
  +
  +
* '''Function definitions'''. The <tt>VarI</tt> constructor of the <tt>Info</tt> type advertises that you might get back the source code for a function definition. In fact, GHC currently (7.4) <em>always</em> returns <tt>Nothing</tt> in this field. It's a bit awkward and no one has really needed it.
   
 
== Why does <tt>runQ</tt> crash if I try to reify something? ==
 
== Why does <tt>runQ</tt> crash if I try to reify something? ==
Line 180: Line 217:
   
 
-----------------
 
-----------------
  +
 
= Examples =
 
= Examples =
 
== Tuples ==
 
== Tuples ==
 
=== Select from a tuple ===
 
=== Select from a tuple ===
   
An example to select an element from a tuple of arbitrary size. Taken from [http://www.haskell.org/th/papers/meta-haskell.ps this paper].
+
An example to select an element from a tuple of arbitrary size. Taken from [https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.189.4479&rep=rep1&type=pdf this paper].
   
 
Use like so:
 
Use like so:
Line 206: Line 244:
 
rhs = varE(as !! (i -1)) -- !! is 0 based
 
rhs = varE(as !! (i -1)) -- !! is 0 based
   
as :: [String]
+
as :: [Name]
as = ["a" ++ show i | i <- [1..n] ]
+
as = [ mkName $ "a" ++ show i | i <- [1..n] ]
 
</haskell>
 
</haskell>
   
Line 216: Line 254:
 
where pat = tupP (map varP as)
 
where pat = tupP (map varP as)
 
rhs = varE (as !! (i - 1))
 
rhs = varE (as !! (i - 1))
as = [ "a" ++ show j | j <- [1..n] ]
+
as = [ mkName $ "a" ++ show j | j <- [1..n] ]
 
</haskell>
 
</haskell>
  +
  +
=== Apply a function to the n'th element ===
  +
  +
<haskell>
  +
tmap i n = do
  +
f <- newName "f"
  +
as <- replicateM n (newName "a")
  +
lamE [varP f, tupP (map varP as)] $
  +
tupE [ if i == i'
  +
then [| $(varE f) $a |]
  +
else a
  +
| (a,i') <- map varE as `zip` [1..] ]
  +
</haskell>
  +
  +
Then tmap can be called as:
  +
  +
> $(tmap 3 4) (+ 1) (1,2,3,4)
  +
(1,2,4,4)
   
 
=== Convert the first n elements of a list to a tuple ===
 
=== Convert the first n elements of a list to a tuple ===
   
This example creates a tuple by extracting elemnts from a list. Taken from
+
This example creates a tuple by extracting elements from a list. Taken from
 
[http://www.xoltar.org/2003/aug/13/templateHaskellTupleSample.html www.xoltar.org]
 
[http://www.xoltar.org/2003/aug/13/templateHaskellTupleSample.html www.xoltar.org]
   
Line 269: Line 325:
   
 
<haskell>
 
<haskell>
  +
data T = T Int String Double
toT :: [Dynamic] -> T
 
  +
  +
toT :: [Dynamic] -> Maybe T
 
toT [a,b,c] = do
 
toT [a,b,c] = do
 
a' <- fromDynamic a
 
a' <- fromDynamic a
Line 275: Line 333:
 
c' <- fromDynamic c
 
c' <- fromDynamic c
 
return (T a' b' c')
 
return (T a' b' c')
  +
toT _ = Nothing
 
</haskell>
 
</haskell>
 
   
 
== Printf ==
 
== Printf ==
  +
Build it using a command similar to:
This example taken from: http://haskell.cs.yale.edu/ghc/docs/6.0/html/users_guide/template-haskell.html
 
 
Build it using a command similar to one of the following (depending on your environment):
 
 
ghc/compiler/stage3/ghc-inplace --make -fglasgow-exts -package haskell-src main.hs -o main.exe
 
ghc --make -fth Main.hs -o printfTest
 
   
  +
ghc --make Main.hs -o main
  +
 
Main.hs:
 
Main.hs:
   
 
<haskell>
 
<haskell>
  +
{-# LANGUAGE TemplateHaskell #-}
module Main where
 
   
-- Import our template "pr"
+
-- Import our template "printf"
import Printf ( pr )
+
import PrintF (printf)
   
 
-- The splice operator $ takes the Haskell source code
 
-- The splice operator $ takes the Haskell source code
-- generated at compile time by "pr" and splices it into
+
-- generated at compile time by "printf" and splices it into
 
-- the argument of "putStrLn".
 
-- the argument of "putStrLn".
main = putStrLn ( $(pr "Hello") )
+
main = do
  +
putStrLn $ $(printf "Hello %s %%x%% %d %%x%%") "World" 12
 
</haskell>
 
</haskell>
   
Printf.hs:
+
PrintF.hs:
   
 
<haskell>
 
<haskell>
  +
{-# LANGUAGE TemplateHaskell #-}
module Printf where
 
  +
module PrintF where
   
-- Skeletal printf from the paper.
+
-- NB: printf needs to be in a separate module to the one where
-- It needs to be in a separate module to the one where
 
 
-- you intend to use it.
 
-- you intend to use it.
   
 
-- Import some Template Haskell syntax
 
-- Import some Template Haskell syntax
import Language.Haskell.TH.Syntax
+
import Language.Haskell.TH
   
  +
-- Possible string tokens: %d %s and literal strings
-- Describe a format string
 
 
data Format = D | S | L String
 
data Format = D | S | L String
  +
deriving Show
  +
  +
-- a poor man's tokenizer
  +
tokenize :: String -> [Format]
  +
tokenize [] = []
  +
tokenize ('%':c:rest) | c == 'd' = D : tokenize rest
  +
| c == 's' = S : tokenize rest
  +
tokenize (s:str) = L (s:p) : tokenize rest -- so we don't get stuck on weird '%'
  +
where (p,rest) = span (/= '%') str
   
  +
-- generate argument list for the function
-- Parse a format string. This is left largely to you
 
  +
args :: [Format] -> [PatQ]
-- as we are here interested in building our first ever
 
  +
args fmt = concatMap (\(f,n) -> case f of
-- Template Haskell program and not in building printf.
 
  +
L _ -> []
parse :: String -> [Format]
 
  +
_ -> [varP n]) $ zip fmt names
parse s = [ L s ]
 
  +
where names = [ mkName $ 'x' : show i | i <- [0..] ]
   
  +
-- generate body of the function
-- Generate Haskell source code from a parsed representation
 
  +
body :: [Format] -> ExpQ
-- of the format string. This code will be spliced into
 
  +
body fmt = foldr (\ e e' -> infixApp e [| (++) |] e') (last exps) (init exps)
-- the module which calls "pr", at compile time.
 
  +
where exps = [ case f of
gen :: [Format] -> ExpQ
 
gen [D] = [| \n -> show n |]
+
L s -> stringE s
gen [S] = [| \s -> s |]
+
D -> appE [| show |] (varE n)
  +
S -> varE n
gen [L s] = stringE s
 
  +
| (f,n) <- zip fmt names ]
  +
names = [ mkName $ 'x' : show i | i <- [0..] ]
   
  +
-- glue the argument list and body together into a lambda
-- Here we generate the Haskell code for the splice
 
  +
-- this is what gets spliced into the haskell code at the call
-- from an input format string.
 
  +
-- site of "printf"
pr :: String -> ExpQ
 
  +
printf :: String -> Q Exp
pr s = gen (parse s)
 
  +
printf format = lamE (args fmt) (body fmt)
  +
where fmt = tokenize format
 
</haskell>
 
</haskell>
   
Line 456: Line 526:
   
 
-- AutrijusTang
 
-- AutrijusTang
  +
  +
== zipWithN ==
  +
  +
Here $(zipn 3) = zipWith3 etc.
  +
  +
<haskell>
  +
import Language.Haskell.TH; import Control.Applicative; import Control.Monad
  +
  +
zipn n = do
  +
vs <- replicateM n (newName "vs")
  +
[| \f ->
  +
$(lamE (map varP vs)
  +
[| getZipList $
  +
$(foldl
  +
(\a b -> [| $a <*> $b |])
  +
[| pure f |]
  +
(map (\v -> [| ZipList $(varE v) |]) vs))
  +
|])
  +
|]
  +
</haskell>
   
 
== 'generic' zipWith ==
 
== 'generic' zipWith ==
Line 492: Line 582:
 
(t, t1, t2, t3) -> (t, t1, t2, t3) -> (t, t1, t2, t3)
 
(t, t1, t2, t3) -> (t, t1, t2, t3) -> (t, t1, t2, t3)
 
</haskell>
 
</haskell>
 
   
 
==[[Template haskell/Instance deriving example|Instance deriving example]]==
 
==[[Template haskell/Instance deriving example|Instance deriving example]]==
Line 501: Line 590:
 
Note that this example assumes that the functions of the class take a parameter that is the same type as instance is parameterized with.
 
Note that this example assumes that the functions of the class take a parameter that is the same type as instance is parameterized with.
   
The message [http://www.haskell.org/pipermail/template-haskell/2006-August/000581.html email message] contains the full source ([http://www.iist.unu.edu/~vs/haskell/TH_render.hs extracted file]).
+
The message [http://www.haskell.org/pipermail/template-haskell/2006-August/000581.html email message] contains the full source ([https://web.archive.org/web/20120206061801/www.iist.unu.edu/~vs/haskell/TH_render.hs extracted file]).
  +
  +
== [[Quasiquotation|QuasiQuoters]] ==
  +
New in ghc-6.10 is -XQuasiQuotes, which allows one to extend GHC's syntax from library code. Quite a few examples were previously part of older [http://hackage.haskell.org/package/haskell-src-meta-0.2 haskell-src-meta]. Some of these are now part of [http://hackage.haskell.org/package/applicative-quoters applicative-quoters]
  +
  +
=== Similarity with splices ===
  +
  +
Quasiquoters used in expression contexts (those using the ''quoteExp'') behave to a first approximation like regular TH splices:
  +
  +
<haskell>
  +
simpleQQ = QuasiQuoter { quoteExp = stringE } -- in another module
  +
  +
[$simpleQQ| a b c d |] == $(quoteExp simpleQQ " a b c d ")
  +
</haskell>
  +
  +
== Generating records which are variations of existing records ==
  +
This example uses [[Scrap your boilerplate]] (SYB) to address some of the pain of dealing with the rather large data types. We define <tt>mkOptional</tt> so that given a data type <tt><datatype></tt> it generates a new data type <tt><datatype>_opt</tt> that is like <tt><datatype></tt>, but where each record field <tt><field> :: <type></tt> is changed to <tt><field>_opt :: Maybe <type></tt>, and all types <tt><type></tt> that are defined in the same module <tt><datatype></tt> are replaced with <tt><type>_opt</tt>.
  +
  +
First an example of using <tt>mkOptional</tt>:
  +
<haskell>
  +
{-# LANGUAGE TemplateHaskell #-}
  +
import A
  +
  +
data Foo = Foo { a :: Double
  +
, f :: Int }
  +
data Bar = Bar { x :: Foo
  +
, y :: String }
  +
  +
mapM mkOptional [''Foo, ''Bar]
  +
</haskell>
  +
  +
The above generates the following new types <tt>Foo_opt</tt> and <tt>Bar_opt</tt>:
  +
<haskell>
  +
data Foo_opt = Foo_opt { a_opt :: Maybe Double
  +
, f_opt :: Maybe Int }
  +
data Bar_opt = Bar_opt { x_opt :: Maybe Foo_opt
  +
, y_opt :: Maybe String }
  +
</haskell>
  +
Note that <tt>x_opt</tt> has type <tt>Maybe Foo_opt</tt>, and not type <tt>Maybe Foo</tt>.
  +
  +
Here is the implementation of <tt>mkOptional</tt>:
  +
<haskell>
  +
{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}
  +
module A where
  +
import Language.Haskell.TH
  +
import Data.Generics
  +
  +
addMaybesAndOpts :: Maybe String -> Dec -> Q Dec
  +
addMaybesAndOpts modName dec =
  +
-- Apply @rename@ and @addMaybe@ everywhere in the
  +
-- declaration @dec@.
  +
--
  +
-- The SYB, @everywhere (mkT (f :: a -> a)) (x :: b)@
  +
-- applies @f@ to all data of type @a@ in @x@, and
  +
-- @everywhereM (mkM (f :: a -> m a) (x :: b)@ is
  +
-- similar, but applies @f@ everywhere in @x@ monadically.
  +
everywhere (mkT rename) <$>
  +
everywhereM (mkM addMaybe) dec
  +
where
  +
-- Add the "_opt" suffix to a name, if it's from
  +
-- the given module.
  +
rename :: Name -> Name
  +
rename n = if nameModule n == modName
  +
then mkName $ nameBase n ++ "_opt"
  +
else n
  +
  +
-- Wrap the type of a record field in @Maybe@.
  +
addMaybe :: (Name, Strict, Type) -> Q (Name, Strict, Type)
  +
addMaybe (n, s, ty) = do
  +
ty' <- [t| Maybe $(return ty) |]
  +
return (n,s,ty')
  +
  +
mkOptional :: Name -> Q Dec
  +
mkOptional n = do
  +
TyConI dec <- reify n
  +
addMaybesAndOpts (nameModule n) dec
  +
</haskell>
  +
   
 
[[Category:Language extensions]]
 
[[Category:Language extensions]]

Latest revision as of 00:29, 15 June 2023

Template Haskell is a GHC extension to Haskell that adds compile-time metaprogramming facilities. The original design can be found here. It is included in GHC since version 6.

The GHC Users Guide has a Template Haskell section.

This page hopes to be a more central and organized repository of TH related things.


What is Template Haskell?

Template Haskell is an extension to Haskell 98 that allows you to do type-safe compile-time meta-programming, with Haskell both as the manipulating language and the language being manipulated.

Intuitively Template Haskell provides new language features that allow us to convert back and forth between concrete syntax, i.e. what you would type when you write normal Haskell code, and abstract syntax trees. These abstract syntax trees are represented using Haskell datatypes and, at compile time, they can be manipulated by Haskell code. This allows you to reify (convert from concrete syntax to an abstract syntax tree) some code, transform it and splice it back in (convert back again), or even to produce completely new code and splice that in, while the compiler is compiling your module.

For email about Template Haskell, use the GHC users mailing list. It's worth joining if you start to use TH.


Template Haskell specification

Template Haskell is only documented rather informally at the moment. Here are the main resources:

Template Haskell tutorials and papers

One reader said "These docs are *brilliant* ! Exactly what I need to get an understanding of TH."

(Note: These documents are from the Wayback machine because the originals disappeared. They're public documents on Google docs, which shouldn't require logging in. However, if you're asked to sign in to view them, you're running into a known Google bug. You can fix it by browsing to Google, presumably gaining a cookie in the process.)

  • Papers about Template Haskell
    • Template metaprogramming for Haskell, by Tim Sheard and Simon Peyton Jones, Oct 2002. [ps]
    • Template Haskell: A Report From The Field, by Ian Lynagh, May 2003. [ps]
    • Unrolling and Simplifying Expressions with Template Haskell, by Ian Lynagh, December 2002. [ps]
    • Automatic skeletons in Template Haskell, by Kevin Hammond, Jost Berthold and Rita Loogen, June 2003. [pdf]
    • Optimising Embedded DSLs using Template Haskell, by Sean Seefried, Manuel Chakravarty, Gabriele Keller, March 2004. [pdf]
    • Typing Template Haskell: Soft Types, by Ian Lynagh, August 2004. [ps]

Other useful resources

Feel free to update our Wikipedia entry http://en.wikipedia.org/wiki/Template_Haskell

Projects

What are you doing/planning to do/have done with Template Haskell?

  • The ForSyDe methodology is currently implemented as a Haskell-based DSL which makes extensive use of Template Haskell.
  • I am writing Template Greencard - a reimplementation of GreenCard using TH. Many bits work out really nicely. A few bits didn't work so nicely - once I get some time to think, I'll try to persuade the TH folk to make some changes to fix some of these. -- AlastairReid
  • I'm writing Hacanon - a framework for automatic generation of C++ bindings. Read "automated Template Greencard for C++" (-: Darcs repo: http://www.ScannedInAvian.org/repos/hacanon - You'll need gccxml (http://www.gccxml.org/) to compile the examples. - 27 Dec Lemmih.
  • Following other FFI tools developers, I see some future for Template HSFFIG, especially when it comes to autogenerate peek and poke methods for structures defined in C; may be useful for implementation of certain network protocols such as X11 where layout of messages is provided as C structure/union declaration. - 16 Dec 2005 DimitryGolubovsky
  • I am using Template Haskell as a mechanism to get parsed, typechecked code into an Ajax based Haskell Equational Reasoning tool Haskell Equational Reasoning Assistant, as well as simplify the specification of equational relationships between pieces of code. There was a quicktime movie of the tool being used on http://www.gill-warbington.com/home/andy/share/hera1.html - AndyGill
  • I am working on functional metaprogramming techniques to enhance programming reliability and productivity, by reusing much of the existing compiler technology. Template Haskell is especially interesting for me because it permits to check size information of structures by the compiler, provided this information is available at compile time. This approach is especially appropriate for hardware designs, where the structures are fixed before the circuit starts operating. See our metaprogramming web page at http://www.infosun.fmi.uni-passau.de/cl/metaprog/ -- ChristophHerrmann(http://www.cs.st-and.ac.uk/~ch)

Utilities

Helper functions, debugging functions, or more involved code e.g. a monadic fold algebra for TH.Syntax.

Known Bugs

Take a look at the open bugs against Template Haskell on the GHC bug tracker.

Wish list

Things that Ian Lynagh (Igloo) mentioned in his paper Template Haskell: A Report From The Field in May 2003 (available here), by section:

  • Section 2 (curses)
    • The ability to splice names (into "foreign import" declarations, in particular)
    • The ability to add things to the export list from a splice(?)
    • The ability to use things defined at the toplevel of a module from splices in that same module (would require multi-stage compilation, as opposed to the current approach of expanding splices during typechecking)
  • Section 3 (deriving instances of classes)
    • First-class reification (the reify function)
    • A way to discover whether a data constructor was defined infix or prefix (which is necessary to derive instances for Read and Show as outlined in The Haskell 98 Report: Specification of Derived Instances) (if there is a way, Derive seems ignorant of it)
    • Type/context splicing (in instance headers in particular)
  • Section 4 (printf)
    • He says something to the effect that a pattern-matching form of the quotation brackets would be cool if it was expressive enough to be useful, but that this would be hard. (Don't expect this anytime soon.)
  • Section 5 (fraskell)
    • Type information for quoted code (so that simplification can be done safely even with overloaded operations, like, oh, (+))
  • Section 6 (pan)
    • Type info again, and strictness info too (this one seems a bit pie-in-the-sky...)

(Please leave the implemented ones here, but crossed off.)

Any other features that may be nice, and TH projects you'd want to see.


Tips and Tricks

What to do when you can't splice that there

When you try to splice something into the middle of a template and find that you just can't, instead of getting frustrated about it, why not use the template to see what it would look like in longhand?

First, an excerpt from a module of my own. I, by the way, am SamB.

{-# OPTIONS_GHC -fglasgow-exts -fth #-}

module MMixMemory where

import Data.Int
import Data.Word

class (Integral int, Integral word)
    => SignConversion int word | int -> word, word -> int where
                               
    toSigned   :: word -> int
    toSigned   = fromIntegral
    toUnsigned :: int -> word
    toUnsigned = fromIntegral

Say I want to find out what I need to do to splice in the types for an instance declaration for the SignConversion class, so that I can declare instances for Int8 with Word8 through Int64 with Word64. So, I start up good-ol' GHCi and do the following:

$ ghci -fth -fglasgow-exts
Prelude> :l MMixMemory
*MMixMemory> :m +Language.Haskell.TH.Syntax
*MMixMemory Language.Haskell.TH.Syntax> runQ [d| instance SignConversion Int Word where |] >>= print
[InstanceD [] (AppT (AppT (ConT MMixMemory.SignConversion) (ConT GHC.Base.Int)) (ConT GHC.Word.Word)) []]

What can reify see?

When you use reify to give you information about a Name, GHC will tell you what it knows. But sometimes it doesn't know stuff. In particular

  • Imported things. When you reify an imported function, type constructor, class, etc, from (say) module M, GHC runs off to the interface file M.hi in which it deposited all the info it learned when compiling M. However, if you compiled M without optimisation (ie -O0, the default), and without -XTemplateHaskell, GHC tries to put as little info in the interface file as possible. (This is a possibly-misguided attempt to keep interface files small.) In particular, it may dump only the name and kind of a data type into M.hi, but not its constructors.
Under these circumstances you may reify a data type but get back no information about its data constructors or fields. Solution: compile M with
  • -O, or
  • -fno-omit-interface-pragmas (implied by -O), or
  • -XTemplateHaskell.
  • Function definitions. The VarI constructor of the Info type advertises that you might get back the source code for a function definition. In fact, GHC currently (7.4) always returns Nothing in this field. It's a bit awkward and no one has really needed it.

Why does runQ crash if I try to reify something?

This program will fail with an error message when you run it:

  main = do info <- runQ (reify (mkName "Bool")) -- more hygenic is: (reify ''Bool)
            putStrLn (pprint info)

Reason: reify consults the type environment, and that is not available at run-time. The type of reify is

  reify :: Quasi m => Q a -> m a

The IO monad is a poor-man's instance of Quasi; it can allocate unique names and gather error messages, but it can't do reify. This error should really be caught statically.

Instead, you can run the splice directly (ex. in ghci -XTemplateHaskell), as the following shows:

GHCi> let tup = $(tupE $ take 4 $ cycle [ [| "hi" |] , [| 5 |] ])
GHCi> :type tup
tup :: ([Char], Integer, [Char], Integer)

GHCi> tup
("hi",5,"hi",5)

GHCi> $(stringE . show =<< reify ''Int)
"TyConI (DataD [] GHC.Types.Int [] [NormalC GHC.Types.I# [(NotStrict,ConT GHC.Prim.Int#)]] [])"

Here's an email thread with more details.


Examples

Tuples

Select from a tuple

An example to select an element from a tuple of arbitrary size. Taken from this paper.

Use like so:

 > $(sel 2 3) ('a','b','c')
 'b'
 > $(sel 3 4) ('a','b','c','d')
 'c'


sel :: Int -> Int -> ExpQ
sel i n = [| \x -> $(caseE [| x |] [alt]) |]
    where alt :: MatchQ
          alt = match pat (normalB rhs) []

          pat :: Pat
          pat = tupP (map varP as)

          rhs :: ExpQ
          rhs = varE(as !! (i -1)) -- !! is 0 based

          as :: [Name]
          as = [ mkName $ "a" ++ show i | i <- [1..n] ]

Alternately:

sel' i n = lamE [pat] rhs
    where pat = tupP (map varP as)
          rhs = varE (as !! (i - 1))
          as  = [ mkName $ "a" ++ show j | j <- [1..n] ]

Apply a function to the n'th element

tmap i n = do
    f <- newName "f"
    as <- replicateM n (newName "a")
    lamE [varP f, tupP (map varP as)] $
        tupE [  if i == i'
                    then [| $(varE f) $a |]
                    else a
               | (a,i') <- map varE as `zip` [1..] ]

Then tmap can be called as:

 > $(tmap 3 4) (+ 1) (1,2,3,4)
 (1,2,4,4)

Convert the first n elements of a list to a tuple

This example creates a tuple by extracting elements from a list. Taken from www.xoltar.org

Use like so:

 > $(tuple 3) [1,2,3,4,5]
 (1,2,3)
 > $(tuple 2) [1,2]
 (1,2)
tuple :: Int -> ExpQ
tuple n = [|\list -> $(tupE (exprs [|list|])) |]
  where
    exprs list = [infixE (Just (list))
                         (varE "!!")
                         (Just (litE $ integerL (toInteger num)))
                    | num <- [0..(n - 1)]]

An alternative that has more informative errors (a failing pattern match failures give an exact location):

tuple :: Int -> ExpQ
tuple n = do
    ns <- replicateM n (newName "x")
    lamE [foldr (\x y -> conP '(:) [varP x,y]) wildP ns] (tupE $ map varE ns)

Un-nest tuples

Convert nested tuples like (a,(b,(c,()))) into (a,b,c) given the length to generate.

unNest n = do
    vs <- replicateM n (newName "x")
    lamE [foldr (\a b -> tupP [varP a , b])
                (conP '() [])
                vs]
         (tupE (map varE vs))


Marshall a datatype to and from Dynamic

This approach is an example of using template haskell to delay typechecking to be able to abstract out the repeated calls to fromDynamic:

data T = T Int String Double

toT :: [Dynamic] -> Maybe T
toT [a,b,c] = do
    a' <- fromDynamic a
    b' <- fromDynamic b
    c' <- fromDynamic c
    return (T a' b' c')
toT _ = Nothing

Printf

Build it using a command similar to:

 ghc --make Main.hs -o main
 

Main.hs:

{-# LANGUAGE TemplateHaskell #-}

-- Import our template "printf"
import PrintF (printf)

-- The splice operator $ takes the Haskell source code
-- generated at compile time by "printf" and splices it into
-- the argument of "putStrLn".
main = do
    putStrLn $ $(printf "Hello %s %%x%% %d %%x%%") "World" 12

PrintF.hs:

{-# LANGUAGE TemplateHaskell #-}
module PrintF where

-- NB: printf needs to be in a separate module to the one where
-- you intend to use it.

-- Import some Template Haskell syntax
import Language.Haskell.TH

-- Possible string tokens: %d %s and literal strings
data Format = D | S | L String
    deriving Show

-- a poor man's tokenizer
tokenize :: String -> [Format]
tokenize [] = []
tokenize ('%':c:rest) | c == 'd' = D : tokenize rest
                      | c == 's' = S : tokenize rest
tokenize (s:str) = L (s:p) : tokenize rest -- so we don't get stuck on weird '%'
    where (p,rest) = span (/= '%') str

-- generate argument list for the function
args :: [Format] -> [PatQ]
args fmt = concatMap (\(f,n) -> case f of
                                  L _ -> []
                                  _   -> [varP n]) $ zip fmt names
    where names = [ mkName $ 'x' : show i | i <- [0..] ]

-- generate body of the function
body :: [Format] -> ExpQ
body fmt = foldr (\ e e' -> infixApp e [| (++) |] e') (last exps) (init exps)
    where exps = [ case f of
                    L s -> stringE s
                    D   -> appE [| show |] (varE n)
                    S   -> varE n
                 | (f,n) <- zip fmt names ]
          names = [ mkName $ 'x' : show i | i <- [0..] ]

-- glue the argument list and body together into a lambda
-- this is what gets spliced into the haskell code at the call
-- site of "printf"
printf :: String -> Q Exp
printf format = lamE (args fmt) (body fmt)
    where fmt = tokenize format

Handling Options with Templates

A common idiom for treating a set of options, e.g. from GetOpt, is to define a datatype with all the flags and using a list over this datatype:

data Options = B1 | B2 | V Integer

options = [B1, V 3]

While it's simple testing if a Boolean flag is set (simply use "elem"), it's harder to check if an option with an argument is set. It's even more tedious writing helper-functions to obtain the value from such an option since you have to explicitely "un-V" each. Here, Template Haskell can be (ab)used to reduce this a bit. The following example provides the module "OptionsTH" which can be reused regardless of the constructors in "Options". Let's start with showing how we'd like to be able to program. Notice that the resulting lists need some more treatment e.g. through "foldl".

Options.hs:

module Main where

import OptionsTH
import Language.Haskell.TH.Syntax

data Options = B1 | B2 | V Int | S String deriving (Eq, Read, Show)

options = [B1, V 3]

main = do
  print foo -- test if B1 set:               [True,False]
  print bar -- test if V present, w/o value: [False,True]
  print baz -- get value of V if available:  [Nothing,Just 3]

foo :: [Bool]
-- Query constructor B1 which takes no arguments
foo = map $(getopt (THNoArg (mkArg "B1" 0))) options

bar :: [Bool]
-- V is a unary constructor. Let mkArg generate the required
-- wildcard-pattern "V _".
bar = map $(getopt (THNoArg (mkArg "V" 1))) options

-- Can't use a wildcard here!
baz :: [(Maybe Int)]
baz = map $(getopt (THArg (conP "V" [varP "x"]))) options

OptionsTH.hs

module OptionsTH where

import Language.Haskell.TH.Syntax

-- datatype for querying options:
-- NoArg: Not interested in value (also applies to Boolean flags)
-- Arg:   Grep value of unary(!) constructor
data Args = THNoArg Pat | THArg Pat

getopt :: Args -> ExpQ
getopt (THNoArg pat) = lamE [varP "y"] (caseE (varE "y") [cons0, cons1])
 where
  cons0 = match pat   (normalB [| True  |]) []
  cons1 = match wildP (normalB [| False |]) []

-- bind "var" for later use!
getopt (THArg pat@(ConP _ [VarP var])) = lamE [varP "y"] (caseE (varE "y") [cons0, cons1])
 where
  cons0 = match pat   (normalB (appE [|Just|] (varE var))) []
  cons1 = match wildP (normalB [|Nothing|]) []

mkArg :: String -> Int -> Pat
mkArg k c = conP k (replicate c wildP)

While the example might look contrived for the Boolean options which could have been tested much easier, it shows how both types of arguments can be treated in a similar way.

Limitations

getopt (THArg pat) is only able to treat unary constructors. See the pattern-binding: It matches exactly a single VarP.

Improvements

The following reduces things even a bit more, though I still don't know if I like it. It only works since c is either 0 or 1.

mkArg k c = conP k (replicate c (varP "x"))

baz = map $(getopt (THArg (mkArg "V" 1)))

-- VolkerStolz

Generic constructor for records

I have a large number of record types like this, of different length:

data PGD = PGD {
    pgdXUnitBase           :: !Word8,
    pgdYUnitBase           :: !Word8,
    pgdXLUnitsperUnitBase  :: !Word16
}

Currently I use GHC's Binary module to read them from files; it can handle types like (Word8, (Word8, Word16)), but there was no easy way to generate the correct amount of "uncurry" calls for automatically grabbing each element.

With Template Haskell, the instance declarations are now written as such:

instance Binary PGD where
    get bh = do a <- get bh ; return $ $(constrRecord PGD) a

Here the trick lies in constrRecord, which is defined as:

constrRecord x = reify exp where
    reify   = \(Just r) -> appE r $ conE $ last args
    exp     = foldl (dot) uncur $ replicate terms uncur
    terms   = ((length args) `div` 2) - 2
    dot x y = (Just $ infixE x (varE ".") y)
    uncur   = (Just [|uncurry|])
    args    = words . show $ typeOf x

-- AutrijusTang

zipWithN

Here $(zipn 3) = zipWith3 etc.

import Language.Haskell.TH; import Control.Applicative; import Control.Monad

zipn n = do
    vs <- replicateM n (newName "vs")
    [| \f ->
        $(lamE (map varP vs)
            [| getZipList $
                $(foldl
                    (\a b -> [| $a <*> $b |])
                    [| pure f |]
                    (map (\v -> [| ZipList $(varE v) |]) vs))
            |])
     |]

'generic' zipWith

A generalization of zipWith to almost any data. Demonstrates the ability to do dynamic binding with TH splices (note 'dyn').

zipCons :: Name -> Int -> [String] -> ExpQ
zipCons tyName ways functions = do
    let countFields :: Con -> (Name,Int)
        countFields x = case x of
            NormalC n (length -> fields) -> (n, fields)
            RecC n (length -> fields) -> (n,fields)
            InfixC _ n _ -> (n,2)
            ForallC _ _ ct -> countFields ct

    TyConI (DataD _ _ _ [countFields -> (c,n)] _) <- reify tyName
    when (n /= length functions) $ fail "wrong number of functions named"
    vs <- replicateM ways $ replicateM n $ newName "x"
    lamE (map (conP c . map varP) vs) $
        foldl (\con (vs,f) ->
                  con `appE`
                    foldl appE
                        (dyn f)
                        (map varE vs))
              (conE c)
              (transpose vs `zip` functions)

This example uses whichever '+' is in scope when the expression is spliced:

:type $(zipCons ''(,,,) 2 (replicate 4 "+"))

  $(zipCons ''(,,,) 2 (replicate 4 "+"))
    :: (Num t, Num t1, Num t2, Num t3) =>
        (t, t1, t2, t3) -> (t, t1, t2, t3) -> (t, t1, t2, t3)

Instance deriving example

An example using a 'deriving function' to generate a method instance per constructor of a type. The deriving function provides the body of the method.

Note that this example assumes that the functions of the class take a parameter that is the same type as instance is parameterized with.

The message email message contains the full source (extracted file).

QuasiQuoters

New in ghc-6.10 is -XQuasiQuotes, which allows one to extend GHC's syntax from library code. Quite a few examples were previously part of older haskell-src-meta. Some of these are now part of applicative-quoters

Similarity with splices

Quasiquoters used in expression contexts (those using the quoteExp) behave to a first approximation like regular TH splices:

simpleQQ = QuasiQuoter { quoteExp = stringE } -- in another module

[$simpleQQ| a b c d |] == $(quoteExp simpleQQ " a b c d ")

Generating records which are variations of existing records

This example uses Scrap your boilerplate (SYB) to address some of the pain of dealing with the rather large data types. We define mkOptional so that given a data type <datatype> it generates a new data type <datatype>_opt that is like <datatype>, but where each record field <field> :: <type> is changed to <field>_opt :: Maybe <type>, and all types <type> that are defined in the same module <datatype> are replaced with <type>_opt.

First an example of using mkOptional:

{-# LANGUAGE TemplateHaskell #-}
import A

data Foo = Foo { a :: Double
               , f :: Int }
data Bar = Bar { x :: Foo
               , y :: String }

mapM mkOptional [''Foo, ''Bar]

The above generates the following new types Foo_opt and Bar_opt:

data Foo_opt = Foo_opt { a_opt :: Maybe Double
                       , f_opt :: Maybe Int }
data Bar_opt = Bar_opt { x_opt :: Maybe Foo_opt
                       , y_opt :: Maybe String }

Note that x_opt has type Maybe Foo_opt, and not type Maybe Foo.

Here is the implementation of mkOptional:

{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}
module A where
import Language.Haskell.TH
import Data.Generics

addMaybesAndOpts :: Maybe String -> Dec -> Q Dec
addMaybesAndOpts modName dec =
  -- Apply @rename@ and @addMaybe@ everywhere in the
  -- declaration @dec@.
  --
  -- The SYB, @everywhere (mkT (f :: a -> a)) (x :: b)@
  -- applies @f@ to all data of type @a@ in @x@, and
  -- @everywhereM (mkM (f :: a -> m a) (x :: b)@ is
  -- similar, but applies @f@ everywhere in @x@ monadically.
  everywhere (mkT rename) <$>
  everywhereM (mkM addMaybe) dec
  where
    -- Add the "_opt" suffix to a name, if it's from
    -- the given module.
    rename :: Name -> Name
    rename n = if nameModule n == modName
        then mkName $ nameBase n ++ "_opt"
        else n
 
    -- Wrap the type of a record field in @Maybe@.
    addMaybe :: (Name, Strict, Type) -> Q (Name, Strict, Type)
    addMaybe (n, s, ty) = do
      ty' <- [t| Maybe $(return ty) |]
      return (n,s,ty')
 
mkOptional :: Name -> Q Dec
mkOptional n = do
    TyConI dec <- reify n
    addMaybesAndOpts (nameModule n) dec