Question

Je me retrouve à écrire beaucoup de code comme

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

Le problème est que le message imprimé peut se synchroniser avec le code exécuté réel. La solution évidente est de générer automatiquement ce code.

Une façon de le faire serait de mettre tout le texte dans un fichier et d'écrire un petit programme qui lit le fichier et génère du code source Haskell à partir de celui-ci. Mais une autre alternative consiste à utiliser le modèle Haskell.

Quelqu'un sait-il comment je vais rédiger une fonction qui prend un String et en génère le code ci-dessus? Je suppose que cela devrait être assez facile, mais ce n'est pas bien documenté.

Était-ce utile?

La solution

Vous pouvez analyser le code Haskell en utilisant le haskell-src-meta forfait. Voici un exemple rapide comment vous pouvez combiner cela avec le modèle 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

Et vous l'utiliseriez comme ça

{-# LANGUAGE QuasiQuotes #-}

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

Autres conseils

Le modèle Haskell ne fournit pas un moyen simple d'analyser les chaînes arbitraires, donc la solution la plus simple consiste probablement à utiliser le préprocesseur C. Cependant, celui intégré en GHC ne prend pas en charge Stridification, nous devons donc passer des options supplémentaires pour utiliser la "vraie" à la place.

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

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

Vous pouvez ensuite l'utiliser comme ceci:

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

Il y a un exemple de eval- Code Haskell semblable à l'utilisation de l'API GHC ici.

Aie. je pensait ce serait facile, mais pour autant que je sache, c'est en fait impossible.

Je m'attendais à ce qu'il y ait une fonction qui transforme une chaîne en expression, mais apparemment, aucune telle fonction n'existe. Il n'y a même pas de fonction pour charger plus de code source du disque. Il semble donc que cette tâche soit en fait impossible! Je suis assez surpris de cela.

La chose la plus proche que je puisse faire est de citer l'expression que je veux exécuter, puis de construire une épissure qui imprime l'expression citée avant de l'exécuter. Cependant, cela me met à la merci de l'expression de GHC assez imprimante. L'étiquette ne sort pas exactement comme je l'ai tapé. (En particulier, il semble remplacer les opérateurs par des noms complètement qualifiés, ce qui est tout simplement douloureux.)

Vous auriez pensé qu'une fonctionnalité comme celle-ci serait assez triviale à implémenter. Le fait qu'il ne soit pas mis en œuvre ne peut donc être attribué qu'à l'une des deux choses:

  1. Personne n'a réellement besoin de cette fonctionnalité. (Eh bien, sauf moi, évidemment.)

  2. Ce n'est pas aussi trivial qu'il n'y paraît. (Par exemple, peut-être que le contexte d'analyse de l'expression est en quelque sorte en quelque sorte en quelque sorte?)

Vous pouvez également utiliser le décharger Package qui a été écrit pour gérer ce cas d'utilisation exact:

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

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

foo = (+)
bar = (+1)

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

Il gère également plus d'une expression séparée par des virgules:

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

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

Bonus: si vous avez installé Nix-Shell (une partie du Gestionnaire de packages NIX) Vous pouvez même l'essayer rapidement avec ce "one-liner":

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top