2012-02-11 14 views
8

Sto scrivendo una semplice classe HashString, che è solo una stringa e il suo hash:Valutare una funzione al momento della compilazione con Template Haskell

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

Ora sto cercando di generare questi al momento della compilazione con qualcosa di simile :

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

voglio l'hash, e l'imballaggio di testo accada in fase di compilazione. Come faccio a fare questo?

Ecco quello che ho provato finora, ma non sono sicuro se il suo diritto, né sono sicuro che fa tutto al momento della compilazione:

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

risposta

14

Il modo in cui hai scritto il codice, nessuna valutazione avverrà in fase di compilazione. Quando si cita un'espressione Haskell con [| ... |], il codice/AST citato è inserita in cui lo si applica, senza alcuna valutazione, in modo da scrivere:

$(hString "hello, world") 

è esattamente lo stesso di scrittura:

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

Ma pensa su di esso in questo modo: si utilizza [| ... |] per citare un'espressione da inserire in seguito e si genera codice in fase di compilazione con $(...). Quindi, se includi del codice $(foo) in un'espressione quotata bla = [| bar $(foo) |], facendo $(bla) verrà generato il codice bar $(foo), che a sua volta valuterà foo in fase di compilazione. Inoltre, per prendere un valore generato in fase di compilazione e generare un'espressione da esso, si utilizza la funzione lift. Quindi, ciò che si vuole fare è questo:

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

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

Questa valuta la funzione di hash al momento della compilazione, dal momento che la giunzione interna viene risolto dopo il giunto esterno è stato risolto. A proposito, usareda Data.String è il modo generico di costruire un certo tipo di dati OverloadedString da un String.

Inoltre, si dovrebbe prendere in considerazione l'idea di fare un quasi-quoter per l'interfaccia HashString. L'uso di quasi-quoter è più naturale di quello che si chiama manualmente le funzioni di splicing (e le hai già usate, il citofono senza nome [| ... |] cita le espressioni di Haskell).

Si potrebbe creare un quasiquoter come questo:

import Language.Haskell.TH.Quote 

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

Questo avrebbe permesso che si scrive HashString s con la seguente sintassi:

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

Ottima risposta! Grazie. –

Problemi correlati