2010-11-30 36 views
7

Supposons que nous souhaitons utiliser ReaderT [(a,b)] sur la monade Maybe, puis nous souhaitons effectuer une recherche dans la liste.Est-ce que les fonctions d'enveloppement dans un transformateur monad doivent être considérées comme une mauvaise pratique?

Maintenant facile, et pas trop moyen rare de c'est:

première possibilité

find a = ReaderT (lookup a) 

Cependant, il ne semble que ce affirme quelque chose non négligeable sur le fonctionnement du transformateur READER . En regardant le code source de Control.Monad.Reader, il est clair que cela fonctionne très bien. Mais je n'ai lu aucune documentation à l'appui. Cependant, nous aurions pu écrire trouver comme ceci:

seconde possibilité

find a = do y <- ask 
      lift (lookup a y) 

idées similaires tiennent pour envelopper MaybeT, StateT, State et Reader. Habituellement, j'écris quelque chose comme le premier exemple, mais la plupart du temps, il est vraiment évident de l'écrire comme le deuxième exemple, et vous pourriez même dire que c'est plus lisible. Donc ma question est la suivante: est-ce que le code comme le premier exemple devrait être considéré comme mauvais?

+2

Vous pouvez également écrire '= trouver un ascenseur. recherche a = << ask', qui est aussi clair (IMHO) que la deuxième option, mais est plus courte. –

+0

ou utilisez fmap: find a = fmap (recherche a) ask = recherche <$> ask – urso

+0

Il est merdique qu'ils aient arrêté d'exporter le constructeur – fuz

Répondre

3

La version actuelle du mtl bibliothèque - qui est basée sur la transformateurs bibliothèque - exporte la fonction reader :: (r -> a) -> Reader r a exactement à cet effet lors de l'utilisation du Reader simple, monade. Nous voyons donc que la conception de la bibliothèque prend en compte cet usage. Comme aucune fonction de ce type n'est fournie pour ReaderT, nous pouvons affirmer avec une certaine confiance que la façon officielle de le faire avec ReaderT est d'utiliser le constructeur directement. Je suis d'accord avec vous si vous dites qu'un readerT :: Monad m => (r -> a) -> ReaderT r m a analogue devrait être ajouté à la bibliothèque.Ce serait bon à la fois pour la cohérence et pour permettre la possibilité de changer la représentation interne un jour sans casser le code de quiconque.

Mais pour l'instant, votre « première possibilité » est le chemin à parcourir.

9

Je pense que le plus gros problème avec la première est:

Si les auteurs mtl (ou autre bibliothèque transformateur que vous utilisez), décident de cesser d'exporter le constructeur de données pour Reader ™ il cessera de fonctionner. Cela est arrivé avec la monade d'État dans la version bump de mtl 1 à mtl 2 et c'est assez ennuyeux. Considérant que, ask fait partie de l'API officielle de Reader et vous devriez planifier sur elle en permanence.

D'un autre côté, je ne considérerais pas la première erreur.

+0

La monade d'état est toujours disponible avec le constructeur. Je pense que vous voulez dire la 'ST'-monade. – fuz

+2

Si vous regardez ici: http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/src/Control-Monad-State-Lazy.html vous verrez que le constructeur de données pour l'État n'est plus exporté. C'est vrai que vous pouvez faire beaucoup de choses pour contourner cela. –

3

Au moins, il existe une différence de vitesse.

J'ai écrit un program, qui utilise une génération aléatoire comme un état et doit générer environ 500 000 valeurs aléatoires en cours d'exécution. Considérons maintenant ces deux fonctions, qui roulent un dé:

random16 = State $ randomR (1,6) -- Using the internal representation 
random16' = do 
      s <- get 
      (r,s') <- randomR (1,6) s 
      put s' 
      return r 

Whith le premier, le programme se déroule en 6 secondes environ, tandis que le second est beaucoup plus lent, prenant environ 8 secondes. Je peux imager, c'est similaire pour le lecteur, donc peut-être utiliser celui-ci au lieu de plus clair quand le temps d'exécution est important. J'ai utilisé la version stricte pour cela.