使用Template Haskell在编译时评估函数 [英] Evaluating a function at compile time with Template Haskell

查看:110
本文介绍了使用Template Haskell在编译时评估函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am writing a simple HashString class, which is just a string and its hash:

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

现在,我正在尝试在编译时生成类似以下内容的文件:

Now I'm trying to generate these at compile time with something like:

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

我希望在编译时进行哈希和文本打包.我该怎么做?

I want the hash, and the text packing to happen at compile time. How do I do this?

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

Here's what I've tried so far, but I'm not sure if its right, nor am I sure it does everything at compile time:

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

推荐答案

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

The way you've written your code, no evaluation will happen at compile-time. When you quote a Haskell expression with [| ... |], the quoted code/AST is inserted where you apply it without any evaluation, so writing:

$(hString "hello, world")

与写作完全相同:

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

但是这样想:您使用[| ... |]引用稍后要插入的表达式,并在编译时使用$(...)生成代码.因此,如果在带引号的表达式bla = [| bar $(foo) |]中包含一些代码$(foo),则执行$(bla)将生成代码bar $(foo),该代码又将在编译时评估foo.另外,要采用您在编译时生成的值并从中生成表达式,请使用

But think about it like this: you use [| ... |] to quote an expression to be inserted later, and you generate code at compile-time with $(...). So, if you include some code $(foo) in a quoted expression bla = [| bar $(foo) |], doing $(bla) will generate the code bar $(foo), which in turn will evaluate foo at compile time. Also, to take a value that you generate at compile time and generate an expression from it, you use the lift function. So, what you want to do is this:

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

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

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

This evaluates the hash function at compile time, since the inner splice is resolved after the outer splice was resolved. By the way, using fromString from Data.String is the generic way of constructing some OverloadedString data type from a String.

此外,您应该考虑为HashString接口创建一个准引用.与手动调用拼接函数相比,使用准引用更自然(而且您已经使用过它们;无名的[| ... |]引用会引用Haskell表达式).

Also, you should consider making a quasi-quoter for your HashString interface. Using quasi-quoters is more natural than manually calling splice functions (And you've already used them; the nameless [| ... |] quoter quotes Haskell expressions).

您将这样创建一个准报价单:

You would create a quasiquoter like this:

import Language.Haskell.TH.Quote

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

这将使您可以使用以下语法编写HashString:

This would let you write HashStrings with this syntax:

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

这篇关于使用Template Haskell在编译时评估函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆