haskell template-haskell

Plantilla Haskell: ¿Existe una función(o sintaxis especial) que analiza una cadena y devuelve Q Exp?



template-haskell (1)

Como todo el mundo ya ha dicho haskell-src-meta proporciona

parsePat :: String -> Either String Pat parseExp :: String -> Either String Exp parseType :: String -> Either String Type parseDecs :: String -> Either String [Dec]

donde Pat , Exp , Type y Dec son los mismos que para Language.Haskell.TH.Syntax .

¿Por qué GHC no expone su propio analizador?

Lo hace. Inicie GHCi con ghci -package ghc ( ghc es un paquete oculto de forma predeterminada) y puede importar el Parser . Tiene funciones para analizar String en AST preliminares (cuyas declaraciones de datos están en HsSyn ) para patrones, expresiones, tipos y declaraciones.

Bien, entonces, ¿por qué no existe una biblioteca que use este analizador y convierta su salida a AST desde template-haskell (la de Language.Haskell.TH.Syntax )?

Mirando dentro de HsSyn , es obvio que el AST no es exactamente el mismo que en Language.Haskell.TH.Syntax . Abra tanto HsExpr como Exp y de lado a lado verá que este último está lleno de tipos como PostTc id <some-other-type> y PostRn id <some-other-type> . A medida que el AST pasa del analizador al renombrador y al comprobador de tipos, estos bits y piezas se rellenan lentamente. Por ejemplo, ¡ni siquiera conocemos los aspectos básicos de los operadores hasta que llegamos a la comprobación de tipos!

Para realizar las funciones que queremos, tendríamos que ejecutar mucho más que solo el analizador (al menos también el renombrador y el comprobador de tipos, quizás más). Imagínese eso: cada vez que desee analizar incluso una expresión pequeña como "1 + 2" , todavía tendrá que escribir y revisar un montón de importaciones. Incluso entonces, volver a utilizar Language.Haskell.TH.Syntax no sería una caminata en el parque: GHC tiene una variedad de peculiaridades, como su propia forma global especial de almacenar nombres e identificadores.

Hmmm ... pero, ¿qué hace GHC con las cuasi cotizaciones?

Esa es la parte genial! A diferencia de Exp , HsExpr tiene HsSplice para representar empalmes. Mira los tipos para los dos primeros constructores:

HsTypedSplice :: id -> LHsExpr id -> HsSplice id. -- things like [|| 1 + 2 ||] HsUntypedSplice :: id -> LHsExpr id -> HsSplice id -- things like [| 1 + 2 |]

Observe que no están almacenando String , ¡ya están almacenando un AST! Los empalmes se analizan al mismo tiempo que el resto de AST. Y al igual que el resto del AST, los empalmes se pasarán al renombrador, al tipo de comprobación, etc. donde se completará la información faltante.

Entonces, ¿es fundamentalmente imposible usar el analizador de GHC?

Probablemente no. Pero sacarlo del resto de GHC puede ser bastante difícil. Si para usar el analizador de GHC también tenemos que ejecutar el comprobador de tipos y el renombrador, puede ser más elegante y simple usar un analizador independiente como haskell-src-exts (que es de lo que depende Haskell-src-meta ) que es capaz de hacer todo en una sola pasada (las reparaciones, por ejemplo, son una de las cosas que debe dar a este analizador por adelantado ).

Estoy tratando de aprender un poco de Template Haskell y Quasi Quotation, y estoy buscando una función que tome un String y lo analice en Q Exp , por lo que el tipo es:

String -> Q Exp

Intenté buscar en Google, pero los resultados que vi tenían que ver con levantar los literales de String a Q Exp , y el más cercano que encontré fue Language.Haskell.TH.dyn que hace exactamente lo que quiero, pero solo para una variable.

¿Hay otras opciones? Por ejemplo, una sintaxis especial? Estoy solo en el proceso de familiarizarme con [||] y $() , ¿entonces quizás también haya algo para este propósito?

Un ejemplo de cómo imagino que funcionaría:

runQ (parse "(1+)") == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing

Además, soy consciente de esto

runQ [| (1+) |] == InfixE (Just (LitE (IntegerL 1))) (VarE GHC.Num.+) Nothing

pero esto no funcionará con cadenas variables porque, comprensiblemente, la cadena interna se toma como un literal.

runQ [| "(1+)" |] == LitE (StringL "(1+)")

Editar (2015-07-25): comencé a usar haskell-src-meta , y parece funcionar bien hasta ahora. Sin embargo, lleva bastante tiempo cabal install (aproximadamente 10 minutos en mi máquina). Lo que es una pena, mi paquete es bastante pequeño y me gustaría que la instalación fuera rápida. ¿Alguien sabe de una solución que tenga dependencias más pequeñas?