99 votes

Comment jouer avec le Contrôle.Monade.L'écrivain en haskell?

Je suis nouveau à la programmation fonctionnelle, et, récemment, de l'apprentissage à Apprendre que Vous avez un Haskell, mais quand je suis allé à travers ce chapitre, je me suis 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 sauvé ces lignes .hs fichier et mais pas réussi à l'importer dans mon ghci qui se plaignait:

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 ":info de la commande":

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, ce devait être quelque chose comme "newtype Écrivain w ..." donc, je suis confus au sujet de la façon de nourrir les données constructeur et un Écrivain.

Je suppose que cela pourrait être une version liées au problème et mon ghci version 7.4.1

128voto

Chris Taylor Points 25079

Le paquet Control.Monad.Writer ne pas exporter les données constructeur Writer. Je suppose que c'était différent quand LYAH a été écrit.

À l'aide de la MonadWriter typeclass dans ghci

Au lieu de cela, vous créez des écrivains à l'aide de l' writer fonction. Par exemple, dans un ghci session 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 écrivains. Je peux demander pour son type:

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

Qui me dit que le type inféré n'est pas une fonction qui renvoie un particulier de l'auteur, mais plutôt quelque chose qui implémente l' MonadWriter type de classe. Je peux maintenant utiliser:

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

(Entrée fait saisie sur une seule ligne). Ici, j'ai spécifié le type d' multWithLog être Writer [String] Int. Maintenant, je peux l'exécuter:

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

Et vous voyez que nous connecter tous les intermédiaires en opérations.

Pourquoi le code écrit comme cela?

Pourquoi s'embêter à créer l' MonadWriter type de classe? La raison en est à faire avec la monade des transformateurs. Comme vous l'avez correctement réalisé, la façon la plus simple à mettre en oeuvre Writer est un newtype wrapper sur le dessus de la paire:

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

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

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

qui se connecte simplement à son entrée. Maintenant, supposons que vous voulez une monade qui a des fonctionnalités de journalisation, mais aussi fait quelque chose d'autre - dire qu'il peut lire d'un environnement trop. Vous mettriez ce que

type RW r w a = ReaderT r (Writer w a)

Maintenant, parce que l'écrivain est à l'intérieur de l' ReaderT monade transformateur, si vous souhaitez vous connecter la sortie, vous ne pouvez pas utiliser tell w (parce que cela ne fonctionne qu'avec déballé écrivains), mais vous devez utiliser lift $ tell w, ce qui "soulève" l' tell de la fonction par le biais de l' ReaderT , de sorte qu'il peut accéder à l'intérieur de l'écrivain monade. Si vous voulait deux couches de transformateurs (supposons que vous vouliez ajouter la gestion des erreurs en tant que bien), alors vous devez utiliser lift $ lift $ tell w. Cela devient vite lourd.

Au lieu de cela, par la définition d'un type de classe, nous pouvons faire toute monade transformateur wrapper autour d'un écrivain dans une instance de l'écrivain lui-même. Par exemple,

instance (Monoid w, MonadWriter w m) => MonadWriter w (ReaderT r m)

c'est, si w est un monoïde, et m est MonadWriter w, alors ReaderT r m est aussi un MonadWriter w. Cela signifie que nous pouvons utiliser l' tell fonction directement sur le transformé monade, sans avoir à se préoccuper explicitement le soulevant par le biais de la monade transformateur.

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