2010-11-09 40 views
15

Disons que j'ai une fonctionCombinant StateT et de l'État monades

f :: State [Int] Int 

et une fonction:

g :: StateT [Int] IO Int 

Je veux utiliser f dans g et passer l'état entre eux. Y a-t-il une fonction de bibliothèque pour
StateT (return . runState f)? Ou en général, étant donné un transformateur monad avec une monade correspondante, y a-t-il une fonction de bibliothèque pour cela?

+0

Je crois que la modification de TomMD est incorrect. Je crois que l'original 'g :: StateT [Int] IO Int' devrait rester. – glguy

+0

J'ai aimé les autres changements, fixé la parenthèse ... – HaskellElephant

+1

Cette question semble être ce que je cherche, mais les réponses sont beaucoup plus compliquées que http://stackoverflow.com/questions/17325485/combining-statet- io-avec-état, qui a fait le travail pour moi. – crockeea

Répondre

5

De façon plus générale, vous essayez d'appliquer une transformation à une couche interne d'une pile de transformateurs. Pour deux monades arbitraires, la signature de type pourrait ressembler à ceci:

fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 a) -> t m1 a -> t m2 a 

Fondamentalement, un niveau supérieur fmap. En fait, il serait probablement encore plus logique de le combiner avec une carte sur le dernier paramètre ainsi:

fmapMT :: (MonadTrans t, Monad m1, Monad m2) => (m1 a -> m2 b) -> t m1 a -> t m2 b 

Il est clair que cela ne va pas être possible dans tous les cas, bien que lorsque la « source » monade c'est Identity c'est plus facile, mais je peux imaginer définir une autre classe de type pour les endroits où ça marche. Je ne pense pas qu'il y ait quelque chose comme ça dans les bibliothèques de transformateurs monad typiques; Cependant, un peu de navigation sur hackage se présente quelque chose de très similaire in the Monatron package:

class MonadT t => FMonadT t where 
    tmap' :: FunctorD m -> FunctorD n -> (a -> b) 
      -> (forall x. m x -> n x) -> t m a -> t n b 

tmap :: (FMonadT t, Functor m, Functor n) => (forall b. m b -> n b) 
     -> t m a -> t n a 
tmap = tmap' functor functor id 

Dans la signature tmap', les FunctorD types sont essentiellement mises en œuvre ad hoc de fmap au lieu d'utiliser Functor instances directement.

En outre, deux constructeurs de type semblables Functor F et G, une fonction avec un type comme (forall a. F a -> G a) décrit a natural transformation de F à G. Il est tout à fait peut-être une autre mise en oeuvre du plan de transformation que vous voulez quelque part dans le paquet category-extras mais je Je ne suis pas sûr de ce que serait la version théorique d'un transformateur de monade, donc je ne sais pas comment on pourrait l'appeler.

Depuis tmap ne nécessite qu'une instance Functor (que tout Monad doit avoir) et une transformation naturelle et toute Monad a une transformation naturelle de la Identity monade fournies par return, la fonction que vous voulez peut être écrit de manière générique pour toute instance de - l'utilisation de la monade "de base" est définie comme un synonyme du transformateur appliqué à Identity, dans tous les cas, ce qui est généralement le cas avec les bibliothèques de transformateurs.

En revenant à votre exemple spécifique, notez que Monatron a effectivement une instance de FMonadT pour StateT.

+0

Je n'ai pas regardé le paquet Monatron. Je vais devoir regarder de plus près pour le juger. J'apprécie votre idée de définir une classe de type pour quand cela fonctionne, quelqu'un peut-il confirmer ou infirmer que Monatron fait cela? – HaskellElephant

4

Une telle fonction n'est pas définissable pour tous les transformateurs monad. La monade Cont r, par exemple, ne peut pas être levée en ContT r IO car cela nécessiterait de transformer une suite dans la monade IO (a -> IO r) en une pure continuation (a -> r).

+0

N'a pas pensé à ça. Comme vous l'avez dit, ce n'est pas possible pour tous les monadansformateurs. Il faudrait donc un type particulier de connexion entre le transformateur et la monade correspondante alors ... – HaskellElephant

+0

La transformation ne passerait-elle pas dans l'autre sens? Puisque le 'r' dans un type de continuation est habituellement polymorphe, vous pouvez juste écrire' (ContT runCont) :: Cont (m) a -> ContT r m a'. –

+0

@camccann J'allais pour une classe généralisée de transformations (Monad m => m a -> (TransformerOf m) m 'a) (abusant un peu de notation). Si vous essayez d'écrire une instance pour (Cont r a -> ContT r m a), vous serez bloqué au point que j'ai décrit. – Heatsink

4

Ce que vous demandez est une cartographie (connue sous le nom de morphisme monade) d'une monade StateT m-StateT n. Je vais utiliser la bibliothèque mmorph, qui fournit un très bel ensemble d'outils pour travailler avec les morphismes de monades.

Pour effectuer la State -> StateT m transform que vous recherchez, nous allons commencer par la définition d'un morphisme de généraliser l'Identity monade embarqué dans State,

generalize :: Monad m => Identity a -> m a 
generalize = return . runIdentity 

Ensuite, nous voudrons soulever ce morphisme d'agir sur la monade intérieure de votre StateT. C'est-à-dire que nous voulons une fonction qui donne un mappage d'une monade à une autre (par exemple notre morphisme generalize), nous donnera une fonction agissant sur la monade de base d'un transformateur monad, par ex. t Identity a -> t m a. Vous trouverez cela ressemble à la fonction hoist de la classe de MFunctormmorph,

hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b 

Mettre les morceaux ensemble,

myAction :: State s Int 
myAction = return 2 

myAction' :: Monad m => StateT s m Int 
myAction' = hoist generalize myAction