
Joel Reymont wrote:
Folks,
I have very uniform Parsec code like this and I'm wondering if I can derive it using TemplateHaskell or DrIFT or some other tool. Any ideas?
Others have given good answers on how to use code-generation. I am more interested in whether code generation is actually necessary for this example. Haskell has good data-manipulation tools, and parsers are a kind of data... First of all, the nullary commands. Here is an abbreviated version with only them: strCall = choice [ do { reserved "NewLine" ; return NewLine } , do { reserved "GetSymbolName" ; return GetSymbolName } , do { reserved "Description" ; return Description } , do { reserved "GetExchangeName" ; return GetExchangeName } , do { reserved "SymbolRoot" ; return SymbolRoot } ] The 'do' syntax is unpleasantly verbose for such simple examples. As a guideline, I personally only use 'do' syntax if there is at least one result to 'capture' (bind) and use elsewhere. Already the code is easier to read if we do something like this: strCall = choice [ reserved "NewLine" >> return NewLine , reserved "GetSymbolName" >> return GetSymbolName , reserved "Description" >> return Description , reserved "GetExchangeName" >> return GetExchangeName , reserved "SymbolRoot" >> return SymbolRoot ] Now this we can make simpler with the very basic 'metaprogramming' built into the 'deriving Show' that haskell has: nullary x = reserved (show x) >> return x strCall = choice ( map nullary [NewLine,GetSymbolName,Description,GetExchangeName,SymbolRoot] ) To do the same for unaries, we need to know which kind of parameter to expect. data paramType = JNum | JBool | JStr paramParser JNum = numExpr paramParser JBool = boolExpr paramParser JStr = strExpr unary x pt = reserved (quasiShow (x undefined)) >> parens (paramParser pt) >>= return . x strCall = choice ( map unary [ELDateToString,TextGetString,LowerStr,UpperStr,Spaces] ) But what is 'quasiShow'? This is the function which maps these constructors to their string representation, without inspecting the argument (so I can safely pass undefined). This perhaps you do need meta-programming for. Although, I think you can write the following: quasiShow = takeWhile (/=' ') . show Feels a bit ugly though :) And now binaries are only slightly more complex (but now I will use 'do' notation): binary x pta ptb = reserved (quasiShow x undefined undefined) >> parens $ do a <- paramParser pta comma b <- paramParser ptb return x a b I'm sure you can work out ternaries. Of course if you want to automatically choose binary, ternary or unary from the definition of the ADT then you're thoroughly back into the world of metaprogramming. The purpose of this message was not to discourage you from metaprogamming, which is a powerful tool, but just to show that haskell is capable of many things which in other languages would be metaprogramming, either entirely without a meta part, or just using the limited built in meta-facilities (i.e. derived instances). Jules