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|]
câu trả lời tuyệt vời! Cảm ơn bạn. –