Showing posts with label roman. Show all posts
Showing posts with label roman. Show all posts

10 June, 2011

roman numeral literals using Template Haskell

I've know about Template Haskell (TH) for ages but never used it. As a diversion from another project, I thought I'd try to implement roman numeral literals for Haskell, using TH -- this seems an easy way to get started without having to dig in too far.

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:
print [ro|MCMXCVIII|]

ro is 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 [|brackets|] will be passed to my code, which can return an arbitrary Haskell expression to be substituted at compile time. Think C-style #define on steroids.

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.hs looks 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 functionparser 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.

09 April, 2011

roman numerals code

I made this roman numeral convert applet years ago (the RCS tag is $Id: Roman.java,v 1.11 2001/01/07 15:12:00 benc Exp $) and mostly left it untended since then. It accounts for 25% .. 50% of the hits on hawaga.org.uk and was brought to the front of my consciousness by someone asking if they could use the source in their school project. Now I feel all embarrassed about the clunky UI on that thing.