使用Template Haskell在编译时评估函数

时间:2012-02-11 20:29:29

标签: haskell template-haskell

我正在编写一个简单的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) |]

1 个答案:

答案 0 :(得分:14)

编写代码的方式,编译时不会进行评估。当您使用[| ... |]引用Haskell表达式时,将引用的代码/ AST插入到您未经任何评估的情况下应用它,所以写道:

$(hString "hello, world")

与写作完全相同:

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

但请考虑这样:您使用[| ... |]引用稍后要插入的表达式,并在编译时使用$(...)生成代码。因此,如果您在引用的表达式$(foo)中包含一些代码bla = [| bar $(foo) |],则执行$(bla)将生成代码bar $(foo),而代码foo将在编译时评估import Data.String (fromString) import Language.Haskell.TH.Syntax hString s = [| HashString $(lift . hash . T.pack $ s) (fromString s) |] 时间。另外,要获取在编译时生成的值并从中生成表达式,请使用lift函数。那么,你想要做的是:

fromString

这将在编译时评估散列函数,因为在解析外部拼接之后解析内部拼接。顺便说一下,使用Data.String中的OverloadedString是从String构建一些HashString数据类型的通用方法。

另外,您应该考虑为[| ... |]接口制作一个准引脚。使用准引号比手动调用splice函数更自然(并且你已经使用过它们;无名的import Language.Haskell.TH.Quote hstr = QuasiQuoter { quoteExp = hString -- Convenient: You already have this function , quotePat = undefined , quoteType = undefined , quoteDec = undefined } 引用引用了Haskell表达式。)

你会创建一个像这样的quasiquoter:

HashString

这将允许您使用以下语法编写{-# LANGUAGE QuasiQuotes #-} myHashString = [hstr|hello, world|]

{{1}}