Question

provenant de la communauté Ocaml, je suis en train d'apprendre un peu de Haskell. La transition va très bien, mais je suis un peu confus avec le débogage. Je mettais (beaucoup de) « printf » dans mon code OCaml, d'inspecter certaines valeurs intermédiaires, ou comme indicateur pour voir où le calcul exactement a échoué.

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

Je trouve aussi le trace Fonction: http://www.haskell.org/haskellwiki/Debugging#Printf_and_friends qui semble exactement ce que je veux, mais je ne comprends pas le type de: il n'y a pas de IO partout! Quelqu'un peut-il me expliquer le comportement de la fonction de trace?

Était-ce utile?

La solution

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

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

Il y a donc IO dans les coulisses, mais unsafePerformIO est utilisé pour échapper à sortir. C'est une fonction qui rompt potentiellement la transparence référentielle que vous pouvez le deviner en regardant son type IO a -> a et aussi son nom.

Autres conseils

trace est tout simplement fait impur. Le point de la monade IO est de préserver la pureté (pas inaperçu IO par le système de type) et de définir l'ordre d'exécution des instructions, qui, autrement, serait pratiquement pas définies par l'évaluation paresseuse.

risques cependant, vous pouvez néanmoins pirater ensemble une IO a -> a, à savoir effectuer impure IO. Ceci est un hack et bien sûr « souffre » de l'évaluation paresseuse, mais est ce que la trace ne fonctionne tout simplement à cause de débogage.

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

  1. Réduire la nécessité pour le débogage des valeurs intermédiaires

    • petite écriture, réutilisables, claires, des fonctions génériques dont la justesse est évidente.
    • Combiner les bonnes pièces en pièces plus correctes.
    • ou essayer des morceaux de manière interactive.
  2. Utilisez des points d'arrêt, etc. (débogage base compilateur)

  3. Utilisez monades génériques. Si votre code est monadique néanmoins écrire indépendante d'une monade de béton. Utilisez type M a = ... au lieu de IO ... plaine. Vous pouvez ensuite combiner facilement monades à travers des transformateurs et mettre un monade de débogage sur le dessus de celui-ci. Même si la nécessité de monades est parti, vous pouvez simplement insérer Identity a pour les valeurs pures.

Pour ce que ça vaut la peine, il y a en fait deux types de « débogage » en cause ici:

  • Enregistrement des valeurs intermédiaires, telles que la valeur d'une sous-expression particulière sur chaque appel à une fonction récursive
  • Contrôle du comportement d'exécution de l'évaluation d'une expression

Dans un langage strict impératif ceux-ci coïncident habituellement. Dans Haskell, ils le font souvent pas:

  • Enregistrement des valeurs intermédiaires peuvent modifier le comportement d'exécution, par exemple en forçant l'évaluation des termes qui seraient autrement mis au rebut.
  • Le processus réel de calcul peut différer considérablement de la structure apparente d'une expression due à la paresse et les sous-expressions partagées.

Si vous voulez juste de tenir un journal des valeurs intermédiaires, il y a plusieurs façons de le faire - par exemple, plutôt que de tout levage dans IO, un simple monade Writer suffit, ce qui est équivalent à faire fonctions renvoient un 2 uplet de leur résultat réel et une valeur d'accumulateur (une sorte de liste, généralement).

Il est aussi généralement pas nécessaire de mettre tout dans la monade, seules les fonctions qui ont besoin d'écrire à la valeur « log » - par exemple, vous pouvez factoriser seulement les sous-expressions qui pourraient avoir besoin faire l'enregistrement, en laissant la logique principal pur, puis réassembler le calcul global en combinant les fonctions pures et des calculs de l'exploitation forestière de la manière habituelle avec fmaps et ainsi de suite. Gardez à l'esprit que Writer est une sorte d'excuse désolé pour une monade: sans aucun moyen de lire de le journal, écrivez uniquement à elle, chaque calcul est logiquement indépendant de son contexte, ce qui le rend plus facile à jongler avec les choses.

Mais dans certains cas, que ce Overkill -. Pour de nombreuses fonctions pures, tout en mouvement à la sous-expressions et d'essayer des choses toplevel dans le REPL fonctionne assez bien

Si vous voulez réellement inspecter le comportement d'exécution du pur code, cependant - par exemple, pour comprendre pourquoi un sous-expression diverge - il est en général aucun moyen de le faire d'un autre code pur - en fait, cela est essentiellement la définition de pureté. Donc, dans ce cas, vous avez pas d'autre choix que d'utiliser des outils qui existent « en dehors » de la langue pure: soit des fonctions impures telles que unsafePerformPrintfDebugging - errr, je veux dire trace - ou un environnement d'exécution modifié, comme le débogueur GHCi <. / p>

trace a aussi tendance à surévaluer son argument pour l'impression, la perte d'un grand nombre des avantages de la paresse dans le processus.

Si vous pouvez attendre jusqu'à ce que le programme soit terminé avant d'étudier la sortie, l'empilement puis un Writer monade est l'approche classique de la mise en œuvre d'un enregistreur. J'utilise cette pour renvoyer un résultat définir à partir du code de HDBC impur.

Eh bien, puisque tout Haskell est construit autour de principe de l'évaluation paresseuse (de sorte que pour des calculs est en effet non déterministe), l'utilisation de ce printf faire très peu de sens en elle.

Si REPL + inspecter les valeurs résultant est vraiment pas assez pour votre mise au point, tout d'emballage dans IO est le seul choix (mais il est pas le chemin de droite de la programmation Haskell).

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