I already wrote some Haskell roman numeral code which gives a function
numeralsToInteger :: String -> Integer. I tweaked its source code a bit (it wasn't a module previously, for example) and can write this:
import Roman main = do putStrLn "roman test 1 (this should print 1998 below)" print $ numeralsToInteger "MCMXCVIII"
What I want to do with TH is shorten the syntax and make the roman->literal conversion happen at compile time instead of at runtime.
I'll try to use TH's quasi-quoter syntax, which should allow me to write:
rois the name I've chosen for my quasiquoter, and the
[...|...|]bracket syntax comes from template haskell quasiquoting.
What will happen is that everything inside those
|]will be passed to my code, which can return an arbitrary Haskell expression to be substituted at compile time. Think C-style
In my case, I'll always be returning an integer literal expression. The value of that literal will be determined by parsing the roman numeral.
I need to define
ro :: Language.Haskell.TH.Quote.QuasiQuoter, and it needs to live in a different module (so that it can be compiled before attempting to parse anything that uses that quasiquoter)
I want to use my quasiquoter to return an expression. They can be used in more contexts than that (for example, when a type is needed). I'll make the other contexts into an
error, which leaves only the hard one that parses and returns my expression. (after doing a bunch of fiddling Agda, it feels like a relief to be able to say "oh, I'll just make that an error).
It turns out that the code is mostly plumbing.
RomanTH.hslooks like this:
module RomanTH where import Roman import Language.Haskell.TH import Language.Haskell.TH.Quote ro :: Language.Haskell.TH.Quote.QuasiQuoter ro = QuasiQuoter parser err err err err = error "Roman numeral quasiquoter cannot be used in this context" parser :: String -> Q Exp parser s = litE (IntegerL (numeralsToInteger s))
which is all types and wiring except the function
parser s = litE (IntegerL (numeralsToInteger s))which means (right-to-left) convert string to an Integer, embed it in a literal type, and embed that inside an expression.
The complete test program looks like this:
import RomanTH main = do putStrLn "roman test 2 (this should print 1998 below)" print [ro|MCMXCVIII|]
and runs like this:
$ ./test2 roman test 2 (this should print 1998 below) 1998
Well, that took all of 36 minutes.