2012-02-11 1 views
8

Я пишу простой класс HashString, который просто строка и его хэш:Оценка функции во время компиляции с Template Haskell

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) 

Но думаю об этом вот так: вы используете [| ... |], чтобы процитировать выражение, которое будет вставлено позже, и вы генерируете код во время компиляции с $(...). Итак, если вы включили код $(foo) в выраженное в кавычках выражение bla = [| bar $(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) |] 

Это вычисляется хэш-функция во время компиляции, так как внутренний стык будет решен после того, как внешние сращивания было решены. Кстати, использование fromString от Data.String является общим способом построения некоторого типа данных OverloadedString с String.

Кроме того, вы должны рассмотреть вопрос о создании квазициклера для вашего интерфейса HashString. Использование квазициклов более естественно, чем ручные вызовы функций сращивания (и вы уже использовали их, безымянный [| ... |] quoter цитирует выражения Haskell).

Вы бы создать quasiquoter так:

import Language.Haskell.TH.Quote 

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

Это позволит вам писать HashString с с этим синтаксисом:

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

Отличный ответ! Спасибо. –