Evaluating a function at compile time with Template Haskell

孤街浪徒 提交于 2020-01-13 08:21:31

问题


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

回答1:


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


来源:https://stackoverflow.com/questions/9243794/evaluating-a-function-at-compile-time-with-template-haskell

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