2012-02-11 34 views
8

Tôi viết một lớp HashString đơn giản, đó chỉ là một chuỗi và băm của nó:Đánh giá một chức năng tại thời gian biên dịch với Template Haskell

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

Bây giờ tôi đang cố gắng để tạo ra những lúc biên dịch với một cái gì đó như :

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

Tôi muốn băm và đóng gói văn bản diễn ra vào thời gian biên dịch. Làm thế nào để tôi làm điều này?

Đây là những gì tôi đã cố gắng cho đến nay, nhưng tôi không chắc chắn nếu quyền của mình, tôi cũng không chắc chắn nó làm mọi thứ tại thời gian biên dịch:

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

Trả lời

14

Cách bạn đã viết mã của bạn, không có đánh giá nào sẽ xảy ra tại thời điểm biên dịch. Khi bạn trích dẫn một biểu Haskell với [| ... |], mã niêm yết/AST được chèn nơi bạn áp dụng nó mà không có bất kỳ đánh giá, vì vậy viết:

$(hString "hello, world") 

là chính xác giống như viết:

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

Nhưng hãy nghĩ về nó như thế này: bạn sử dụng [| ... |] để trích dẫn một biểu thức được chèn sau này và bạn tạo mã tại thời gian biên dịch với $(...). Vì vậy, nếu bạn bao gồm một số mã $(foo) trong biểu thức được trích dẫn bla = [| bar $(foo) |], làm $(bla) sẽ tạo mã bar $(foo), do đó sẽ đánh giá foo lúc biên dịch. Ngoài ra, để lấy một giá trị mà bạn tạo ra tại thời gian biên dịch và tạo ra một biểu thức từ nó, bạn sử dụng hàm lift. Vì vậy, những gì bạn muốn làm là:

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

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

Điều này đánh giá hàm băm tại thời gian biên dịch, vì mối nối bên trong được giải quyết sau khi mối nối bên ngoài được giải quyết. Nhân tiện, sử dụng fromString từ Data.String là cách chung để xây dựng một số loại dữ liệu OverloadedString từ một số String.

Ngoài ra, bạn nên cân nhắc tạo một quasi-quoter cho giao diện HashString của mình. Sử dụng quasi-quoters là tự nhiên hơn so với cách gọi thủ công các chức năng ghép nối (Và bạn đã sử dụng chúng; không có tên là [| ... |] quoter trích dẫn các biểu thức Haskell).

Bạn sẽ tạo ra một quasiquoter như thế này:

import Language.Haskell.TH.Quote 

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

này sẽ cho phép bạn viết HashString s với cú pháp sau:

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

câu trả lời tuyệt vời! Cảm ơn bạn. –

Các vấn đề liên quan