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

Cla*_*bel 9 haskell template-haskell

我正在编写一个简单的HashString类,它只是一个字符串及其哈希:

data HashString = HashString Int    -- ^ hash
                             T.Text -- ^ string!
Run Code Online (Sandbox Code Playgroud)

现在我正在尝试在编译时使用以下内容生成这些:

$(hString "hello, world") :: HashString
Run Code Online (Sandbox Code Playgroud)

我想要哈希,文本打包在编译时发生.我该怎么做呢?

这是我到目前为止所尝试的,但我不确定它是否正确,我也不确定它是否在编译时完成了所有工作:

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

dfl*_*str 14

编写代码的方式,编译时不会进行评估.当您引用Haskell表达式时[| ... |],引用的代码/ AST将被插入您应用它而不进行任何评估,因此编写:

$(hString "hello, world")
Run Code Online (Sandbox Code Playgroud)

与写作完全相同:

let s = "hello, world" in HashString (hash $ T.pack s) (T.pack s)
Run Code Online (Sandbox Code Playgroud)

但想想这样:你用[| ... |]引用一个表达式稍后插入,并在编译时生成代码$(...).因此,如果$(foo)在引用的表达式中包含一些代码bla = [| bar $(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) |]
Run Code Online (Sandbox Code Playgroud)

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

此外,您应该考虑为您的HashString界面制作一个准引号.使用准[| ... |]引号比手动调用splice函数更自然(并且你已经使用过它们;无名引号引用了Haskell表达式).

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

import Language.Haskell.TH.Quote

hstr =
  QuasiQuoter
  { quoteExp = hString -- Convenient: You already have this function
  , quotePat = undefined
  , quoteType = undefined
  , quoteDec = undefined
  }
Run Code Online (Sandbox Code Playgroud)

这将允许您HashString使用以下语法编写s:

{-# LANGUAGE QuasiQuotes #-}
myHashString = [hstr|hello, world|]
Run Code Online (Sandbox Code Playgroud)