Comment jouer avec Control.Monad.Writer en haskell?

Je suis novice en functional programming et récemment en train d’apprendre à Learn a a Haskell , mais lorsque j’ai parcouru ce chapitre , je me suis retrouvé avec le programme ci-dessous:

import Control.Monad.Writer logNumber :: Int -> Writer [Ssortingng] Int logNumber x = Writer (x, ["Got number: " ++ show x]) multWithLog :: Writer [Ssortingng] Int multWithLog = do a <- logNumber 3 b <- logNumber 5 return (a*b) 

J’ai sauvegardé ces lignes dans un fichier .hs mais je n’ai pas réussi à l’importer dans mes ghci qui se plaignaient:

 more1.hs:4:15: Not in scope: data constructor `Writer' Perhaps you meant `WriterT' (imported from Control.Monad.Writer) Failed, modules loaded: none. 

J’ai examiné le type par la commande “: info”:

 Prelude Control.Monad.Writer> :info Writer type Writer w = WriterT w Data.Functor.Identity.Identity -- Defined in `Control.Monad.Trans.Writer.Lazy' 

De mon sharepoint vue, cela devait être quelque chose comme “newtype Writer wa …”, donc je ne comprends pas comment nourrir le constructeur de données et obtenir un Writer.

Je suppose que cela pourrait être un problème lié à la version et ma version ghci est 7.4.1

Le package Control.Monad.Writer pas le constructeur de données Writer . Je suppose que c’était différent quand LYAH a été écrit.

Utilisation de la classe de fonts MonadWriter dans ghci

Au lieu de cela, vous créez des rédacteurs en utilisant la fonction writer . Par exemple, dans une session ghci je peux faire

 ghci> import Control.Monad.Writer ghci> let logNumber x = writer (x, ["Got number: " ++ show x]) 

Maintenant, logNumber est une fonction qui crée des auteurs. Je peux demander son type:

 ghci> :t logNumber logNumber :: (Show a, MonadWriter [Ssortingng] m) => a -> ma 

Ce qui me dit que le type déduit n’est pas une fonction qui retourne un écrivain particulier , mais plutôt tout ce qui implémente la classe de type MonadWriter . Je peux maintenant l’utiliser:

 ghci> let multWithLog = do { a <- logNumber 3; b <- logNumber 5; return (a*b) } :: Writer [String] Int 

(Entrée réellement entrée sur une seule ligne). Ici, j'ai spécifié le type de multWithLog comme Writer [Ssortingng] Int . Maintenant je peux l'exécuter:

 ghci> runWriter multWithLog (15, ["Got number: 3","Got number: 5"]) 

Et vous voyez que nous enregistrons toutes les opérations intermédiaires.

Pourquoi le code est-il écrit comme ça?

Pourquoi s'embêter à créer la classe de type MonadWriter ? La raison est à faire avec les transformateurs monad. Comme vous l’avez bien compris, la manière la plus simple d’implémenter Writer est d’utiliser un wrapper newtype sur une paire:

 newtype Writer wa = Writer { runWriter :: (a,w) } 

Vous pouvez déclarer une instance de monade pour cela, puis écrire la fonction

 tell :: Monoid w => w -> Writer w () 

qui enregistre simplement son entrée. Maintenant, supposons que vous vouliez une monade dotée de fonctions de journalisation, mais qui fasse aussi autre chose - dites qu'elle peut aussi lire dans un environnement. Vous devez implémenter cela comme

 type RW rwa = ReaderT r (Writer wa) 

Maintenant que le rédacteur se trouve dans le transformateur ReaderT , si vous voulez consigner la sortie, vous ne pouvez pas utiliser tell w (car cela ne fonctionne qu'avec des ReaderT ) mais vous devez utiliser lift $ tell w , qui "soulève" la fonction tell . à travers le ReaderT pour qu'il puisse accéder à la monade d'écriture interne. Si vous vouliez des transformateurs à deux couches (disons que vous vouliez également append une gestion des erreurs), vous devrez utiliser lift $ lift $ tell w . Cela devient vite difficile à manier.

Au lieu de cela, en définissant une classe de type, nous pouvons transformer n'importe quel transformateur de transformateur monad autour d'un graveur en instance de graveur lui-même. Par exemple,

 instance (Monoid w, MonadWriter wm) => MonadWriter w (ReaderT rm) 

c'est-à-dire que si w est un monoïde et m est un MonadWriter w , alors ReaderT rm est également un MonadWriter w . Cela signifie que nous pouvons utiliser la fonction tell directement sur la monade transformée, sans avoir à se soucier explicitement de la faire passer dans le transformateur monad.

Une fonction appelée “writer” est disponible à la place d’un constructeur “Writer”. Changement:

logNumber x = Writer (x, ["Got number: " ++ show x])

à:

logNumber x = writer (x, ["Got number: " ++ show x])

J’ai reçu un message similaire en essayant le LYAH “Pour quelques monades supplémentaires ” en utilisant l’éditeur en ligne de Haskell dans le fichier repl.it

J’ai changé l’importation depuis:

 import Control.Monad.Writer 

à:

 import qualified Control.Monad.Trans.Writer.Lazy as W 

Donc, mon code fonctionne maintenant comme ceci (avec l’inspiration du blog Haskell de Kwang ):

 import Data.Monoid import qualified Control.Monad.Trans.Writer.Lazy as W output :: Ssortingng -> W.Writer [Ssortingng] () output x = W.tell [x] gcd' :: Int -> Int -> W.Writer [Ssortingng] Int gcd' ab | b == 0 = do output ("Finished with " ++ show a) return a | otherwise = do output (show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)) gcd' b (a `mod` b) main :: IO() main = mapM_ putStrLn $ snd $ W.runWriter (gcd' 8 3) 

Le code est actuellement exécutable ici