2012-02-11 56 views
8

我寫一個簡單的HashString類,這僅僅是一個字符串,其哈希函數:評估在編譯時使用模板哈斯克爾

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

現在我想要在編譯時的東西,如產生這些:

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

我想散列,文本包裝發生在編譯時。我該怎麼做呢?

這是我到目前爲止已經試過,但我不知道,如果它的正確的,我也不是肯定它在編譯的時候一切:

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

回答

14

你寫代碼的方式,編譯時不會進行評估。當你引用一個Haskell表達[| ... |],所報碼/ AST插入您應用它沒有任何評價,所以寫:

$(hString "hello, world") 

是完全一樣的文字:

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

但認爲它是這樣的:你用[| ... |]引用稍後插入的表達式,你產生在編譯時有$(...)代碼。所以,如果你包括在引述表達bla = [| bar $(foo) |]一些代碼$(foo),做$(bla)將生成的代碼bar $(foo),這反過來將在編譯時評估foo。此外,拍攝您在編譯時產生的值,並且從它的表達,可以使用lift功能。所以,你想要做什麼是這樣的:

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

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

這種評估在編譯時哈希函數,因爲外部拼接得到解決後,內部拼接解決。順便說一句,使用fromStringData.String是從String構建一些OverloadedString數據類型的通用方法。

此外,你應該考慮做一個準報價者爲您HashString接口。採用準quoters比手動調用拼接功能更自然(你已經使用過;無名[| ... |]加引號引用哈斯克爾表達式)。

您可以創建這樣一個quasiquoter:

import Language.Haskell.TH.Quote 

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

這將讓你寫HashString s與這句法:

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

出色答卷!謝謝。 – 2012-02-11 21:31:52