Generate a function using Template Haskell -
is possible define function using template haskell? example
convertstringtovalue :: string -> int convertstringtovalue "three" = 3 convertstringtovalue "four" = 4
i have map [char] int
.
fromlist [("five",5),("six",6)]
how can add functions
convertstringtovalue "six" = 6 convertstringtovalue "five" = 5
at compile time using template haskell , map
? appears quite silly use template haskell purpose know nevertheless.
you can using 2 files:
a "maker" file: maker.hs
:
module maker {-# language templatehaskell #-} import language.haskell.th maker items = x <- newname "x" lame [varp x] (casee (vare x) (map (\(a,b) -> match (litp $ stringl a) (normalb $ lite $ integerl b) []) items))
and main file: main.hs
:
{-# language templatehaskell #-} import language.haskell.th import maker function = $(maker [("five",5),("six",6)])
in case function
of type [char] -> int
, compiled as:
\x -> case x of "five" -> 5 "six" -> 6
it if have written:
function = \x -> case x of "five" -> 5 "six" -> 6
yourself. evidently that's not going pay off 2 or 3 cases, have written in question yourself, when want use thousands of cases, or list of items generated list comprehension, starts pay off.
making template haskell yourself
this section aims briefly describe how write template haskell yourself. tutorial not "a complete introduction to...": there other techniques this.
in order write template haskell, can first try few expressions, , try generalize them using map
, fold
, etc.
analyze ast tree
first better take on how haskell parse expression itself. can runq
, brackets [| ... |]
...
expression wish analyze. instance:
$ ghci -xtemplatehaskell ghci, version 7.6.3: http://www.haskell.org/ghc/ :? loading package ghc-prim ... linking ... done. loading package integer-gmp ... linking ... done. loading package base ... linking ... done. prelude> :m language.haskell.th prelude language.haskell.th> runq [| \x -> case x of "five" -> 5; "six" -> 6 |] loading package array-0.4.0.1 ... linking ... done. loading package deepseq-1.3.0.1 ... linking ... done. loading package containers-0.5.0.0 ... linking ... done. loading package pretty-1.1.1.0 ... linking ... done. loading package template-haskell ... linking ... done. lame [varp x_0] (casee (vare x_0) [match (litp (stringl "five")) (normalb (lite (integerl 5))) [],match (litp (stringl "six")) (normalb (lite (integerl 6))) []])
the ast thus:
lame [varp x_0] (casee (vare x_0) [match (litp (stringl "five")) (normalb (lite (integerl 5))) [],match (litp (stringl "six")) (normalb (lite (integerl 6))) []])
so have derived abstract syntax tree (ast) expression. hint make expressions generic enough. instance use multiple cases in case
block, since using single case doesn't tell how should add second 1 expression. wish create such abstract syntax tree ourselves.
create variable names
a first aspect variables, varp x_0
, vare x_0
. cannot copy-paste them. here x_0
name. in order make sure don't use name exists, can use newname
. can construct following expression replicate it:
maker = x <- newname "x" return $ lame [varp x] (casee (vare x) [match (litp (stringl "five")) (normalb (lite (integerl 5))) [],match (litp (stringl "six")) (normalb (lite (integerl 6))) []])
generalize function
evidently not interested in constructing fixed abstract syntax tree, otherwise have written ourselves. point introduce 1 or more variables, , reason variables. every tuple ("five",5)
, etc. introduce match
statement:
match (litp (stringl "five")) (normalb (lite (integerl 5))) []
now can generalize \(a,b)
:
\(a,b) -> match (litp (stringl a)) (normalb (lite (integerl b))) []
and use map
iterate on items:
map (\(a,b) -> match (litp (stringl a)) (normalb (lite (integerl b))) []) items
with items
list of tuples wish generate cases. we're done:
maker items = x <- newname "x" return $ lame [varp x] (casee (vare x) (map (\(a,b) -> match (litp (stringl a)) (normalb (lite (integerl b))) []) items))
now can omit return
because library has lowercase variants these items. can furthermore try "cleanup" code little bit (like instance (normalb (lite (integerl b)))
(normalb $ lite $ integerl b)
, etc.); instance using hlint
.
maker items = x <- newname "x" lame [varp x] (casee (vare x) (map (\(a,b) -> match (litp $ stringl a) (normalb $ lite $ integerl b) []) items))
the maker here kind of function makes/constructs function.
careful infinite lists
be aware compiler evaluate in between dollar brackets $()
. if instance use infinite list:
function = $(maker [(show i,i)|i<-[1..]]) -- don't this!
this keep allocating memory abstract syntax tree , run out of memory. compiler not expand ast @ run time.
Comments
Post a Comment