2012-02-11 7 views
8

Yo escribo una clase HashString simple, que es simplemente una cadena y su hash:evaluar una función en tiempo de compilación con Plantilla Haskell

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

Ahora estoy tratando de generar estos en tiempo de compilación con algo como :

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

Quiero que el hash y el empaquetado de texto sucedan en tiempo de compilación. ¿Cómo hago esto?

Aquí es lo que he probado hasta ahora, pero no estoy seguro de si es correcto, ni estoy seguro de que lo hace todo en tiempo de compilación:

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

Respuesta

14

La forma en que has escrito el código, ninguna evaluación ocurrirá en tiempo de compilación. Cuando se cita una expresión Haskell con [| ... |], se inserta el código/AST citado donde se aplica sin ningún tipo de evaluación, por lo que la escritura:

$(hString "hello, world") 

es exactamente lo mismo que escribir:

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

Pero piensa al respecto de esta manera: utiliza [| ... |] para citar una expresión que se insertará más tarde y genera código en tiempo de compilación con $(...). Por lo tanto, si se incluye algún código $(foo) en una expresión citada bla = [| bar $(foo) |], haciendo $(bla) generará el código bar $(foo), que a su vez evaluar foo en tiempo de compilación. Además, para tomar un valor que genere en tiempo de compilación y generar una expresión a partir de él, utilice la función lift. Por lo tanto, lo que se quiere hacer es lo siguiente:

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

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

Esto evalúa la función hash en tiempo de compilación, ya que el empalme interno se resuelve después de que se resolviera el empalme exterior. Por cierto, el uso de fromStringData.String es la forma genérica de construir algún tipo de datos de un OverloadedStringString.

También, usted debe considerar la posibilidad de un cuasi-Quoter para su interfaz de HashString. El uso de cuasi-quot es más natural que llamar manualmente a las funciones de empalme (y ya las has usado; el anónimo [| ... |] cita las expresiones de Haskell).

Para ello se crea un quasiquoter así:

import Language.Haskell.TH.Quote 

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

Esto permitirá escribir HashString s con esta sintaxis:

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

Excelente respuesta! Gracias. –

Cuestiones relacionadas