2012-02-11 55 views
8

我写一个简单的HashString类,这仅仅是一个字符串,其哈希函数:评估在编译时使用模板哈斯克尔

data HashString = HashString Int --^hash 
          T.Text --^string! 

现在我想要在编译时的东西,如产生这些:

$(hString "hello, world") :: HashString 

我想散列,文本包装发生在编译时。我该怎么做呢?

这是我到目前为止已经试过,但我不知道,如果它的正确的,我也不是肯定它在编译的时候一切:

hString :: String -> Q Exp 
hString s = [| HashString (hash $ T.pack s) (T.pack s) |] 

回答

14

你写代码的方式,编译时不会进行评估。当你引用一个Haskell表达[| ... |],所报码/ AST插入您应用它没有任何评价,所以写:

$(hString "hello, world") 

是完全一样的文字:

let s = "hello, world" in HashString (hash $ T.pack s) (T.pack s) 

但认为它是这样的:你用[| ... |]引用稍后插入的表达式,你产生在编译时有$(...)代码。所以,如果你包括在引述表达bla = [| bar $(foo) |]一些代码$(foo),做$(bla)将生成的代码bar $(foo),这反过来将在编译时评估foo。此外,拍摄您在编译时产生的值,并且从它的表达,可以使用lift功能。所以,你想要做什么是这样的:

import Data.String (fromString) 
import Language.Haskell.TH.Syntax 

hString s = [| HashString $(lift . hash . T.pack $ s) (fromString s) |] 

这种评估在编译时哈希函数,因为外部拼接得到解决后,内部拼接解决。顺便说一句,使用fromStringData.String是从String构建一些OverloadedString数据类型的通用方法。

此外,你应该考虑做一个准报价者为您HashString接口。采用准quoters比手动调用拼接功能更自然(你已经使用过;无名[| ... |]加引号引用哈斯克尔表达式)。

您可以创建这样一个quasiquoter:

import Language.Haskell.TH.Quote 

hstr = 
    QuasiQuoter 
    { quoteExp = hString -- Convenient: You already have this function 
    , quotePat = undefined 
    , quoteType = undefined 
    , quoteDec = undefined 
    } 

这将让你写HashString s与这句法:

{-# LANGUAGE QuasiQuotes #-} 
myHashString = [hstr|hello, world|] 
+0

出色答卷!谢谢。 – 2012-02-11 21:31:52