2013-08-28 6 views
2

J'ai une fonction qui compose deux monades:Pourquoi `hoist` contraint-il le paramètre type de cette monade?

comp :: Monad m => m a -> m b -> m b 

Et deux exemples de ces monades, où le est « à l'intérieur » un Mfunctor,

ms :: Monad m => m String 
ms = undefined 

tma :: (Monad m, MFunctor t) => t m a 
tma = undefined 

Maintenant, si je tente de composer ms avec tma :

tmas = hoist (\ma -> comp ma ms) tma 

Je reçois cette erreur:

Could not deduce (a ~ [Char]) 
    from the context (Monad m, MFunctor t) 
     bound by the inferred type of 
       comp :: (Monad m, MFunctor t) => t m b 
     at Coroutine.hs:607:1-40 
     `a' is a rigid type variable bound by 
      a type expected by the context: m a -> m a at Coroutine.hs:607:8 
    Expected type: m a 
     Actual type: m String 

qui stipule que a dans ms doit être de type quelconque: ms :: Monad m => m a.

Pourquoi est-ce et est-il un moyen de composer tma avec des monades de paramètres spécifiques.

Je vois que la signature du palan est:

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

mais ne peut pas imaginer comment forall AFFECTE ce que je suis en train de le faire, si elle a un effet.

Répondre

3

intervertir l'ordre des arguments à comp comme ceci:

tmas = hoist (\ma -> comp ms ma) tma 

-- or more simply: 
tmas = hoist (comp ms) tma 

la raison est que le type de comp est:

comp :: (Monad m) => m a -> m b -> m b 

Si vous définissez, ms comme second argument, le type-chèques comme Stringb et vous obtenez:

(`comp` ms) :: (Monad m) => m a -> m String 

...mais si vous définissez ms comme premier argument, les a de type-chèques comme String vous obtenez:

(ms `comp`) :: (Monad m) => m b -> m b 

Ce dernier type est le type correct pour hoist depuis le b est universellement quantifié (ie "forall" ed) .

Pour répondre à votre question sur l'exactitude, la réponse est que la quantification universelle garantit que l'argument de hoist ne modifie que la couche de monade et non la valeur de retour de monade. Cependant, si vous avez l'intention de modifier la valeur de retour, alors hoist n'est pas ce que vous voulez.

2

Le type hoist indique qu'il attend une fonction (forall a. m a -> n a), c'est-à-dire une fonction qui modifie le type "conteneur" mais conserve le même paramètre de type. Le forall signifie ici que la fonction que vous fournissez ne peut pas être spécialisée pour un a spécifique mais doit fonctionner pour n'importe quel paramètre de type.

La fonction que vous essayez d'utiliser (\ma -> comp ma ms) a le type m a -> m String donc il est à peu près le contraire de ce que hoist attend, car il maintient le récipient (m) le même, mais change le paramètre de type (a-String) .

Je pense que ce que vous cherchez réellement à la place de hoist dans ce cas est une fonction qui soulève une fonction monadique pour travailler sur monades transformées, donc au lieu de MFunctor vous voulez quelque chose comme:

import Control.Monad.Trans.Class 

tmas :: (Monad m, Monad (t m), MonadTrans t) => t m String 
tmas = transLift (\ma -> comp ma ms) tma 

transLift :: (Monad m, Monad (t m), MonadTrans t) => (m a -> m b) -> t m a -> t m b 
transLift f tma = tma >>= lift . f . return 
+0

Merci pour l'explication. La présence de 'forall' garantit-elle un certain niveau de correction? – chibro2

Questions connexes