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) |]
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 HashString
s with this syntax:
{-# LANGUAGE QuasiQuotes #-}
myHashString = [hstr|hello, world|]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With