Evaluating a function at compile time with Template Haskell

南笙酒味 提交于 2019-12-05 02:12:34

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")

is exactly the same as writing:

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

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) |]

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.

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
  }

This would let you write HashStrings with this syntax:

{-# LANGUAGE QuasiQuotes #-}
myHashString = [hstr|hello, world|]
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!