Je suis nouveau dans la programmation fonctionnelle et j'ai récemment appris à Learn You a Haskell , mais quand j'ai parcouru ce chapitre , je suis resté coincé avec le programme ci-dessous:
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b)
J'ai enregistré ces lignes dans un fichier .hs et je n'ai pas réussi à l'importer dans mon ghci, ce qui s'est plaint:
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 point de vue, c'était censé être quelque chose comme "newtype Writer wa ..." donc je ne sais pas comment alimenter 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
Réponses:
Le package
Control.Monad.Writer
n'exporte pas le constructeur de donnéesWriter
. Je suppose que c'était différent lorsque LYAH a été écrit.Utilisation de la classe de types MonadWriter dans ghci
Au lieu de cela, vous créez des rédacteurs à l'aide de la
writer
fonction. Par exemple, dans une session ghci, je peux faireC'est maintenant
logNumber
une fonction qui crée des écrivains. Je peux demander son type:Ce qui me dit que le type inféré n'est pas une fonction qui renvoie un écrivain particulier , mais plutôt tout ce qui implémente la
MonadWriter
classe de type. Je peux maintenant l'utiliser:(Entrée en fait entré tout sur une seule ligne). Ici, j'ai spécifié le type d'
multWithLog
êtreWriter [String] Int
. Maintenant je peux l'exécuter:Et vous voyez que nous enregistrons toutes les opérations intermédiaires.
Pourquoi le code est-il écrit comme ça?
Pourquoi se donner la peine de créer la
MonadWriter
classe de type? La raison est à voir avec les transformateurs monades. Comme vous l'avez bien compris, le moyen le plus simple d'implémenterWriter
est de créer un wrapper de nouveau type au-dessus d'une paire:Vous pouvez déclarer une instance de monade pour cela, puis écrire la fonction
qui enregistre simplement son entrée. Supposons maintenant que vous vouliez une monade qui ait des capacités de journalisation, mais qui fasse également autre chose - disons qu'elle peut également lire à partir d'un environnement. Vous implémenteriez cela comme
Maintenant, parce que l'écrivain est à l'intérieur du
ReaderT
transformateur monade, si vous voulez enregistrer la sortie, vous ne pouvez pas utilisertell w
(car cela ne fonctionne qu'avec des écrivains non emballés) mais vous devez utiliserlift $ tell w
, ce qui "soulève" latell
fonction à travers leReaderT
afin qu'elle puisse accéder au monade d'écrivain intérieur. Si vous vouliez des transformateurs à deux couches (disons que vous vouliez également ajouter la gestion des erreurs), vous devez les utiliserlift $ lift $ tell w
. Cela devient rapidement difficile à manier.Au lieu de cela, en définissant une classe de type, nous pouvons transformer n'importe quel wrapper de transformateur monade autour d'un écrivain en une instance de writer lui-même. Par exemple,
c'est-à-dire que si
w
est un monoïde, etm
est aMonadWriter w
, alorsReaderT r m
est également aMonadWriter w
. Cela signifie que nous pouvons utiliser latell
fonction directement sur la monade transformée, sans avoir à se soucier de la soulever explicitement à travers le transformateur de monade.la source
mtl
passage de la version majeure 1. * à 2. *, peu de temps après l'écriture de LYAH et RWH. Moment extrêmement malheureux qui a conduit et conduit à beaucoup de confusion chez les débutants.Control.Monad.Trans.Writer
. En plus le type delogNumber
c'estlogNumber :: (Show a, Monad m) => a -> WriterT [[Char]] m a
pour moi.mtl
installé la bibliothèque (ce qui signifie probablement que vous avez une installation de base de GHC, comme minGHC, plutôt que la plate-forme Haskell). À partir d'une invite de commande, exécutezcabal update
etcabal install mtl
réessayez.writer
est utilisé à la place deWriter
ce dernier, la valeur ctor, n'est pas exporté par le module, alors que le premier l'est, et il peut être utilisé pour créer la même valeur que vous créeriez avec le ctor, mais le fait ne pas autoriser la correspondance de modèle.Une fonction appelée "writer" est mise à disposition à la place d'un constructeur "Writer". Changement:
logNumber x = Writer (x, ["Got number: " ++ show x])
à:
logNumber x = writer (x, ["Got number: " ++ show x])
la source
J'ai reçu un message similaire en essayant le LYAH "Pour quelques monades en plus" en utilisant l'éditeur en ligne Haskell dans repl.it
J'ai changé l'importation de:
à:
Donc, mon code fonctionne maintenant comme ceci (avec l'inspiration du blog Haskell de Kwang ):
Le code est actuellement exécutable ici
la source