2012-02-29 13 views
6

me encuentro escribiendo un montón de código comoImprimir y ejecutar una cadena

 
putStr "foo (bar 1) (bar 2) =" 
print $ foo (bar 1) (bar 2) 

El problema es que el mensaje impreso puede perder la sincronización con el código ejecutado real. La solución obvia es generar automáticamente este código.

Una forma de hacerlo sería poner todo el texto en un archivo y escribir un pequeño programa que lea el archivo y genere el código fuente de Haskell. Pero otra alternativa es usar Template Haskell.

¿Alguien sabe cómo escribiría una función que toma un String y genera el código anterior? Supongo que debería ser bastante fácil, pero TH no está bien documentado.

+3

que haría uso de CPP. Crudo pero efectivo para este tipo de cosas. – augustss

+0

CPP funciona - hasta que el texto que desea citar se extiende a más de una línea ... – MathematicalOrchid

+3

"Me encuentro escribiendo mucho código como [esto]" ... ¿por qué? –

Respuesta

8

puede analizar el código Haskell usando el paquete haskell-src-meta. Aquí hay un ejemplo rápido de cómo puedes combinar esto con Template Haskell.

{-# LANGUAGE TemplateHaskell #-} 

import Language.Haskell.TH 
import Language.Haskell.TH.Quote 
import Language.Haskell.Meta 

runShow = QuasiQuoter 
    { quoteExp = runShowQQ 
    , quotePat = undefined 
    , quoteType = undefined 
    , quoteDec = undefined 
    } 

runShowQQ :: String -> Q Exp 
runShowQQ s = do 
    let s'   = s ++ " = " 
     Right exp = parseExp s 
     printExp = appE [|print|] (return exp) 
    infixApp [|putStr s'|] [|(>>)|] printExp 

Y usted utilizar de esta manera

{-# LANGUAGE QuasiQuotes #-} 

[runShow|foo (bar 1) (bar 2)|] 
+3

Entonces, ¿qué estás diciendo es que alguien ha implementado un analizador Haskell como un quasi quoter, y puedes usar eso? (Me encanta cómo dice "aún no está completo" ...). Parece una pena no poder tratar el analizador Haskell de GHC como un quasi quoter en primer lugar ... Me pregunto por qué ¿Apoyar eso? – MathematicalOrchid

+0

Esto aparentemente es lo mejor que se puede hacer con TH, así que voy a aceptar esta respuesta. – MathematicalOrchid

1

Hay un ejemplo de eval -como Haskell code using GHC API here.

4

Plantilla Haskell no proporciona un medio sencillo de analizar cadenas arbitrarias, por lo que la solución más simple es probablemente utilizar el preprocesador C. Sin embargo, el incorporado en GHC no es compatible con la cadena, por lo que necesitamos pasar opciones adicionales para usar el "real" en su lugar.

{-# LANGUAGE CPP #-} 
{-# OPTIONS_GHC -pgmP cpp #-} 

#define PRINT_EXP(x) (putStr #x >> putStr " = " >> print (x)) 

A continuación, puede utilizar de esta manera:

PRINT_EXP(foo (bar 1) (bar 2)) 
+0

Esperaba que TH admitiera el análisis de una cadena, pero como dices, no es así. Lo más inesperado. Noté que usas el CPP "real"; te das cuenta de que no hay uno en Windows, ¿verdad? – MathematicalOrchid

0

Ay. I pensamiento esto sería fácil, pero hasta donde puedo decir, en realidad es imposible.

Estaba esperando que haya una función que convierte una cadena en una expresión, pero aparentemente no existe tal función. Ni siquiera hay una función para cargar más código fuente desde el disco. ¡Así que parece que esta tarea es realmente imposible! Estoy bastante sorprendido por eso.

Lo más parecido que pude hacer es citar la expresión que quiero ejecutar, y luego crear un empalme que imprime bastante la expresión entrecomillada antes de ejecutarla. Sin embargo, eso me pone a merced de la bonita impresora de expresión de GHC. La etiqueta no sale exactamente cuando lo escribí. (En particular, parece reemplazar a los operadores con nombres totalmente calificados, lo cual es simplemente doloroso.)

Habría pensado que una característica como esta sería bastante trivial de implementar. El hecho de que no se implemente solo puede atribuirse a una de estas dos cosas:

  1. Nadie realmente necesita esta función. (Bueno, excepto yo, obviamente).

  2. No es tan trivial como parece. (Por ejemplo, tal vez averiguar de qué contexto analizar la expresión es complicado de alguna manera?)

+1

"es realmente imposible" - mal. "no existe tal función [que convierte una cadena en una expresión]" - incorrecta. La respuesta de shang implementa ambos. Su TH emite la cadena entrecomillada * exactamente * tal como fue ingresada. De hecho, implementó la cosa * exacta * que pediste, produciendo precisamente ese código. Solo funciona para expresiones puras y visibles, y las convierte en un 'IO()', pero eso se puede modificar fácilmente: http://hpaste.org/64551 –

+1

Solo funciona para las expresiones de Haskell que el analizador Haskell de terceros funciona para, no para cualquier expresión que GHC pueda analizar. – MathematicalOrchid

0

También puede utilizar el paquete dump que fue escrito para manejar este exacto de casos de uso:

{-# language QuasiQuotes #-} 
import Debug.Dump 

main = putStrLn [d| foo (bar 1) (bar 2) |] 

foo = (+) 
bar = (+1) 

que imprime: (foo (bar 1) (bar 2)) = 5

También maneja más de una expresión separados por comas:

putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |] 

Which prints: (foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

Bono: Si tiene instalado nix-shell (parte de la nix package manager) incluso se puede probar que fuera rápidamente con este "de una sola línea":

$ nix-shell -p "nix-shell -p "haskellPackages.ghcWithPackages (p: [p.dump])" --run "echo '{-# language QuasiQuotes #-}; import Debug.Dump; foo = (+); bar = (+1); main = putStrLn [d| foo (bar 1) (bar 2), map bar [1, 2] |]' | runhaskell"

Cuestiones relacionadas