Pregunta

Me encuentro escribiendo mucho código como

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

El problema es que el mensaje impreso puede no estar sincronizado 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 a partir de él.Pero otra alternativa es utilizar Template Haskell.

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

¿Fue útil?

Solución

Puede analizar el código Haskell usando el haskell-src-meta paquete.A continuación se muestra un ejemplo rápido de cómo podría 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 lo usarías así

{-# LANGUAGE QuasiQuotes #-}

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

Otros consejos

Template Haskell no proporciona un medio sencillo para analizar cadenas arbitrarias, por lo que la solución más sencilla probablemente sea utilizar el preprocesador de C.Sin embargo, el integrado en GHC no admite la encadenamiento, por lo que debemos 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))

Luego puedes usarlo así:

PRINT_EXP(foo (bar 1) (bar 2))

Hay un ejemplo de eval-como código Haskell usando API GHC aquí.

Ay.I pensamiento Esto sería fácil, pero por lo que sé, en realidad es imposible.

Esperaba que hubiera una función que convirtiera una cadena en una expresión, pero aparentemente no existe tal función.Ni siquiera existe una función para cargar más código fuente desde el disco.¡Parece que esta tarea es realmente imposible!Estoy bastante sorprendido por eso.

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

Habrías pensado que una característica como esta sería bastante trivial de implementar.Por lo tanto, el hecho de que no se implemente sólo puede atribuirse a una de dos cosas:

  1. En realidad, nadie necesita esta función.(Bueno, excepto yo, obviamente).

  2. No es tan trivial como parece.(Por ejemplo, ¿quizás descubrir en qué contexto analizar la expresión sea complicado de alguna manera?)

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

{-# 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 separada por comas:

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

Que imprime: (foo (bar 1) (bar 2)) = 5 (map bar [1, 2]) = [2,3]

Prima:Si tiene instalado nix-shell (parte del administrador de paquetes nix) incluso puedes probarlo rápidamente con este "frase breve":

$ 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"

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top