Difference between revisions of "Template Haskell"
Line 340: | Line 340: | ||
== Instance derving example == |
== Instance derving example == |
||
− | [/Template haskell/Instance deriving example] |
+ | [[/Template haskell/Instance deriving example]] |
[[Category:Language]] |
[[Category:Language]] |
Revision as of 18:46, 28 August 2006
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 included in GHC version 6. If you start working with Template Haskell you'll probably want to join the Template Haskell mailing list.
This page hopes to be a more central and organized repository of TH related things. However, at this point most things should probably go to/through the mailing list first.
Template Haskell Resources
- Template haskell web page: http://www.haskell.org/th/
- The user manual section: http://www.haskell.org/ghc/docs/latest/html/users_guide/template-haskell.html
- The Template Haskell API: http://www.haskell.org/ghc/docs/latest/html/libraries/haskell-src/Language.Haskell.THSyntax.html
- A Template Haskell tutorial: http://www.haskell.org/hawiki/TemplateHaskellTutorial
- The original Template Haskell paper http://research.microsoft.com/~simonpj/papers/meta-haskell/
- Notes on Template Haskell version 2, which describes changes since the original paper: http://research.microsoft.com/~simonpj/tmp/notes2.ps
- The Template Haskell mailing list http://haskell.org/mailman/listinfo/template-haskell
Old and probably not too useful for most but maybe... http://www.cse.unsw.edu.au/~chak/haskell/ghc/comm/exts/th.html
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?
- 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 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.
- 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, as well as simplify the specification of equational relationships between pieces of code. There is 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
Utilities
Helper functions, debugging functions, or more involved code e.g. a monadic fold algebra for THSyntax.
Known Bugs
Take a look at the open bugs against Template Haskell on the GHC bug tracker.
The bug you're most likely to run into is Template Haskell does not work with profiling.
Wish list
Any other features that may be nice, and TH projects you'd want to see.
- 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.
- 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).
- A set of simple examples on this wiki page
- A TH T-shirt with new logo to wear at conferences
- (Long-term) Unify Language.Haskell.Syntax with Language.Haskell.TH.Syntax so there's just one way to do things
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)) []]
Examples
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 :: [String]
as = ["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 = [ "a" ++ show j | j <- [1..n] ]
Convert the first n elements of a list to a tuple
This example creates a tuple by extracting elemnts 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)]]
Printf
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
Main.hs:
module Main where
-- Import our template "pr"
import Printf ( pr )
-- The splice operator $ takes the Haskell source code
-- generated at compile time by "pr" and splices it into
-- the argument of "putStrLn".
main = putStrLn ( $(pr "Hello") )
Printf.hs:
module Printf where
-- Skeletal printf from the paper.
-- It needs to be in a separate module to the one where
-- you intend to use it.
-- Import some Template Haskell syntax
import Language.Haskell.THSyntax
-- Describe a format string
data Format = D | S | L String
-- Parse a format string. This is left largely to you
-- as we are here interested in building our first ever
-- Template Haskell program and not in building printf.
parse :: String -> [Format]
parse s = [ L s ]
-- Generate Haskell source code from a parsed representation
-- of the format string. This code will be spliced into
-- the module which calls "pr", at compile time.
gen :: [Format] -> ExpQ
gen [D] = [| \n -> show n |]
gen [S] = [| \s -> s |]
gen [L s] = stringE s
-- Here we generate the Haskell code for the splice
-- from an input format string.
pr :: String -> ExpQ
pr s = gen (parse s)
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.THSyntax
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.THSyntax
-- 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