Friday, March 27, 2009

Less code - more functionality

Yesterday I tried to implement one "simple" function. It should take a Haskell source and return a list of all parameters to the function
abc :: String->String
E.g. for the part of code:

xy = (abc "hello") ++ (abc "world")

main = do putStrLn (abc "hi")
putStrLn xy
putStrLn (abc "bye")

output should be:
["hello", "world", "hi", "bye"]

Haskell has library Language.Haskell.Parser to parse its own source files, but the output has very complex structure. For example, previous part of code, will be represented like:

(HsModule (SrcLoc {srcFilename = "", srcLine = 1, srcColumn = 1})
(Module "Main") (Just [HsEVar (UnQual (HsIdent "main"))]) []
[HsPatBind (SrcLoc {srcFilename = "", srcLine = 1, srcColumn = 1})
(HsPVar (HsIdent "xy")) (HsUnGuardedRhs (HsInfixApp (HsParen (HsApp (HsVar (UnQual
(HsIdent "abc"))) (HsLit (HsString "hello")))) (HsQVarOp (UnQual (HsSymbol "++")))
(HsParen (HsApp (HsVar (UnQual (HsIdent "abc"))) (HsLit (HsString "world")))))) [],
HsPatBind (SrcLoc {srcFilename = "", srcLine = 3, srcColumn = 1})
(HsPVar (HsIdent "main")) (HsUnGuardedRhs (HsDo [HsQualifier (HsApp (HsVar
(UnQual (HsIdent "putStrLn"))) (HsParen (HsApp (HsVar (UnQual (HsIdent "abc")))
(HsLit (HsString "hi"))))),HsQualifier (HsApp (HsVar (UnQual (HsIdent "putStrLn")))
(HsVar (UnQual (HsIdent "xy")))),HsQualifier (HsApp (HsVar (UnQual (HsIdent "putStrLn")))
(HsParen (HsApp (HsVar (UnQual (HsIdent "abc"))) (HsLit (HsString "bye")))))])) []])

Ugghhhh, looks terrible. The straightforward way to solve my task is to write a bunch of functions that will parse all datatypes, until they extract something like
(HsApp (HsVar (UnQual (HsIdent "abc"))) (HsList (HsString s)))
Maybe it simplier to regexp through haskell code?

No, and let me introduce TemplateHaskell. I haven't used it yet, but heard, that it is very powerfull part of the Haskell. It works like C++ Templates or macros, i.e. during program compilation. On the Haskell-Cafe Neil Mitchel pointed me to the use uniplate generic library. Without deep explorations and understanding how it work, I wrote a one-line function that solves my problem:

getParamList hscode= [x |
HsApp (HsVar (UnQual (HsIdent "abc"))) (HsList (HsString x)) <-
universeBi (parseModule hscode)]

A really brilliant result. Even beginner haskeller could easily understand what is happen here.

PS: It is amazing how fast and helpful haskell community is. Thank you guys :)

No comments:

Post a Comment