Pergunta

Eu me pego escrevendo muitos códigos como

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

O problema é que a mensagem impressa pode ficar fora de sincronia com o código realmente executado.A solução óbvia é gerar automaticamente esse código.

Uma maneira de fazer isso seria colocar todo o texto em um arquivo e escrever um pequeno programa que leia o arquivo e gere o código-fonte Haskell a partir dele.Mas outra alternativa é usar o Template Haskell.

Alguém sabe como eu escreveria uma função que leva um String e gera o código acima a partir dele?Acho que deve ser bem fácil, mas o TH não está bem documentado.

Foi útil?

Solução

Você pode analisar o código Haskell usando o haskell-src-meta pacote.Aqui está um exemplo rápido de como você pode combinar isso com 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

E você usaria assim

{-# LANGUAGE QuasiQuotes #-}

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

Outras dicas

Template Haskell não fornece um meio direto de analisar strings arbitrárias, então a solução mais simples é provavelmente usar o pré-processador C.No entanto, o integrado no GHC não suporta stringificação, então precisamos passar opções extras para usar o "real".

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

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

Você pode então usá-lo assim:

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

Há um exemplo de eval-como código Haskell usando API GHC aqui.

Ai.EU pensamento isso seria fácil, mas até onde sei, na verdade é impossível.

Eu esperava que houvesse uma função que transformasse uma string em uma expressão, mas aparentemente essa função não existe.Não existe nem uma função para carregar mais código-fonte do disco.Então parece que esta tarefa é realmente impossível!Estou bastante surpreso com isso.

A coisa mais próxima que posso fazer é citar a expressão que desejo executar e, em seguida, criar uma emenda que imprima a expressão citada antes de executá-la.No entanto, isso me deixa à mercê da bela impressora de expressão do GHC.O rótulo não sai exatamente enquanto eu digitava.(Em particular, parece substituir os operadores por nomes totalmente qualificados, o que é simplesmente doloroso.)

Você pensaria que um recurso como esse seria bastante trivial de implementar.O facto de não ser implementado só pode, portanto, ser atribuído a uma de duas coisas:

  1. Na verdade, ninguém precisa desse recurso.(Bem, exceto eu, obviamente.)

  2. Não é tão trivial quanto parece.(Por exemplo, talvez descobrir em que contexto analisar a expressão seja complicado de alguma forma?)

Você também pode usar o jogar fora pacote que foi escrito para lidar com este caso de uso exato:

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

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

foo = (+)
bar = (+1)

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

Também lida com mais de uma expressão separada por vírgulas:

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]

Bônus:Se você tiver o nix-shell instalado (parte do gerenciador de pacotes nix) você pode até experimentá-lo rapidamente com esta "linha única":

$ 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top