62 votes

Comment "déboguer" Haskell avec printfs?

venant de l'Ocaml de la communauté, je suis en train d'apprendre un peu de Haskell. La transition se passe très bien, mais je suis un peu confus avec le débogage. J'ai utilisé pour mettre (beaucoup de) "printf" dans mon code ocaml, pour inspecter certains des valeurs intermédiaires, ou comme indicateur pour voir où le calcul exactement échoué.

Depuis printf est une IO action, je dois lever mon code haskell à l'intérieur de l' IO monade pour être en mesure à ce genre de débogage ? Ou est-il une meilleure façon de le faire (je ne veux vraiment pas le faire à la main si elle peut être évitée)

Je trouve aussi la trace de la fonction : http://www.haskell.org/haskellwiki/Debugging#Printf_and_friends ce qui semble exactement ce que je veux, mais je ne comprends pas ce genre: il n'y a pas d' IOn'importe où! Quelqu'un peut-il m'expliquer le comportement de la fonction de trace ?

56voto

Daniel Velkov Points 9244

trace est le plus facile à utiliser la méthode à des fins de débogage. Ce n'est pas en IO exactement pour la raison, vous l'avez souligné: pas besoin de soulever votre code dans l' IO monade. Il est mis en œuvre comme ceci

trace :: String -> a -> a
trace string expr = unsafePerformIO $ do
    putTraceMsg string
    return expr

Il est donc IO derrière les scènes, mais unsafePerformIO est utilisé pour s'échapper. C'est une fonction qui, potentiellement, des sauts de transparence référentielle qui vous pouvez le deviner en regardant son type IO a -> a et également son nom.

17voto

Dario Points 26259

trace est simplement rendue impure. Le point de l' IO monade est de préserver la pureté (pas d'IO inaperçu par le type de système) et de définir l'ordre d'exécution des instructions, qui devraient autrement être pratiquement pas défini par le biais de l'évaluation différée.

À ses risques et périls cependant, vous pouvez néanmoins hack ensemble quelques - IO a -> a, c'est à dire effectuer impur IO. C'est un hack et bien sûr "souffre" de l'évaluation différée, mais c'est ce que la trace est tout simplement pour des raisons de débogage.

Néanmoins, vous devriez probablement passer d'autres moyens pour le débogage:

1) la Réduction de la nécessité pour le débogage des valeurs intermédiaires

  • Écrire de petits, réutilisable, clair, de fonctions génériques dont la justesse est évident.
  • Combiner les bonnes pièces pour plus de bonnes pièces.
  • Écrire des tests ou à essayer des pièces de manière interactive

2)

  • Utiliser les points d'arrêt, etc. (par le compilateur en fonction de débogage)

3)

  • Utilisation générique de monades. Si votre code est monadique néanmoins, d'écriture, il est indépendant d'un béton monade. Utiliser type M a = ... au lieu de plain IO .... Vous pouvez ensuite combiner facilement les monades par le biais de transformateurs et de mettre un débogage monade sur le dessus. Même si la nécessité de monades est parti, vous avez juste à insérer Identity a pure et simple des valeurs.

14voto

C. A. McCann Points 56834

Pour ce que ça vaut, il y a en fait deux sortes de "débogage" en question ici:

  • La journalisation des valeurs intermédiaires, telles que la valeur d'un particulier de la sous-expression a sur chaque appel à une fonction récursive
  • Inspecter le comportement d'exécution de l'évaluation d'une expression

Dans un strict impératif de langue coïncident généralement. En Haskell, souvent, ils ne sont pas:

  • L'enregistrement des valeurs intermédiaires peuvent modifier le comportement d'exécution, comme en forçant l'évaluation des termes qui seraient autrement jetés.
  • Le processus de calcul peuvent considérablement différer de l'apparente la structure d'une expression en raison à la paresse et partagé des sous-expressions.

Si vous voulez garder un journal des valeurs intermédiaires, il existe de nombreuses façons de le faire, par exemple, plutôt que de soulever le tout dans IO, un simple Writer monade suffira, cela étant l'équivalent de fonctions de prise de retour d'un 2-tuple de leur résultat réel et un accumulateur de valeur (une sorte de liste de, en général).

Il est aussi généralement pas nécessaire de mettre tout dans la monade, seules les fonctions qui ont besoin d'écrire pour le "journal" de valeur", par exemple, vous pouvez le facteur de la sous-expressions qui pourraient avoir besoin de faire de journalisation, laissant la principale logique pure, puis remonter l'ensemble du calcul en combinant les fonctions pures et la journalisation des calculs de la manière habituelle, avec fmaps et autres joyeusetés. Gardez à l'esprit que Writer est une sorte de triste excuse pour un monade: pas moyen de lire à partir du journal, seulement écrire, chaque calcul est logiquement indépendante de son contexte, ce qui rend plus facile de jongler avec les choses autour de vous.

Mais dans certains cas, de même que overkill--pour de nombreuses fonctions pures, simplement en déplaçant les sous-expressions de la racine et en essayant des choses dans le REPL fonctionne assez bien.

Si vous voulez inspecter le temps d'exécution de comportement de code pur, cependant, par exemple, de comprendre pourquoi une sous-expression diverge--il n'y a en général aucun moyen de le faire à partir d'autres code pur--en fait, c'est essentiellement la définition de la pureté. Donc, dans ce cas, vous n'avez pas d'autre choix que d'utiliser les outils qui existent "en dehors" de la pure langue: soit impur fonctions telles que unsafePerformPrintfDebugging--euh, je veux dire trace--ou une modification de l'environnement d'exécution, tels que le GHCi débogueur.

2voto

yatima2975 Points 4191

trace également tendance à surévaluer son argument en faveur de l'impression, perdant de ce fait beaucoup d'avantages de la paresse.

0voto

Gaius Points 1223

Si vous pouvez attendre que le programme soit terminé avant d'étudier le résultat, empiler une monade Writer est l'approche classique de la mise en œuvre d'un enregistreur. J'utilise ceci ici pour renvoyer un ensemble de résultats à partir de code HDBC impur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X