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:is exactly the same as writing:
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 expressionbla = [| bar $(foo) |], doing$(bla)will generate the codebar $(foo), which in turn will evaluatefooat compile time. Also, to take a value that you generate at compile time and generate an expression from it, you use theliftfunction. So, what you want to do is this:This evaluates the hash function at compile time, since the inner splice is resolved after the outer splice was resolved. By the way, using
fromStringfromData.Stringis the generic way of constructing someOverloadedStringdata type from aString.Also, you should consider making a quasi-quoter for your
HashStringinterface. 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:
This would let you write
HashStrings with this syntax: