Auparavant, je pensais avoir trouvé des exemples de monades explicitement définies sans transformateur, mais ces exemples étaient incorrects.
Le transformateur pour Either a (z -> a)
est m (Either a (z -> m a)
, donde m
est une monade étrangère arbitraire. Le transformateur pour (a -> n p) -> n a
es (a -> t m p) -> t m a
donde t m
est le transformateur de la monade n
.
- La monade libre pointue.
Le constructeur de type monade L
pour cet exemple est défini par
type L z a = Either a (z -> a)
L'intention de cette monade est d'embellir la monade ordinaire du lecteur. z -> a
avec un pure
valeur ( Left x
). La monade du lecteur ordinaire pure
est une fonction constante pure x = _ -> x
. Cependant, si l'on nous donne une valeur de type z -> a
nous ne serons pas en mesure de déterminer si cette valeur est une fonction constante. Avec L z a
le pure
est représentée explicitement comme Left x
. Les utilisateurs peuvent désormais effectuer une recherche par motif sur L z a
et déterminer si une valeur monadique donnée est pure ou a un effet. En dehors de cela, la monade L z
fait exactement la même chose que la monade de lecture.
L'instance de la monade :
instance Monad (L z) where
return x = Left x
(Left x) >>= f = f x
(Right q) >>= f = Right(join merged) where
join :: (z -> z -> r) -> z -> r
join f x = f x x -- the standard `join` for Reader monad
merged :: z -> z -> r
merged = merge . f . q -- `f . q` is the `fmap` of the Reader monad
merge :: Either a (z -> a) -> z -> a
merge (Left x) _ = x
merge (Right p) z = p z
Cette monade L z
est un cas spécifique d'une construction plus générale, (Monad m) => Monad (L m)
donde L m a = Either a (m a)
. Cette construction embellit une monade donnée m
en ajoutant un pure
valeur ( Left x
), de sorte que les utilisateurs peuvent désormais effectuer une recherche de motif sur L m
pour décider si la valeur est pure. Dans tous les autres cas, L m
représente le même effet de calcul que la monade m
.
L'instance de la monade pour L m
est presque la même que pour l'exemple ci-dessus, sauf que le fichier join
y fmap
de la monade m
doivent être utilisées, et la fonction d'aide merge
est défini par
merge :: Either a (m a) -> m a
merge (Left x) = return @m x
merge (Right p) = p
J'ai vérifié que les lois de la monade tiennent pour L m
avec une monade arbitraire m
.
Cette construction donne le foncteur pointu libre sur la monade donnée m
. Cette construction garantit que le foncteur pointu libre sur un monade est également un monade.
Le transformateur pour la monade libre pointue est défini comme suit :
type LT m n a = n (Either a (mT n a))
où mT
est le transformateur de monade de la monade m (qui doit être connue).
- Un autre exemple :
type S a = (a -> Bool) -> Maybe a
Cette monade est apparue dans le contexte des "monades de recherche". aquí . Le site papier de Jules Hedges mentionne également la monade de recherche, et plus généralement, les monades de "sélection" de la forme
type Sq n q a = (a -> n q) -> n a
pour une monade donnée n
et un type fixe q
. La monade de recherche ci-dessus est un cas particulier de la monade de sélection avec n a = Maybe a
y q = ()
. L'article de Hedges affirme (sans preuve, mais il l'a prouvé plus tard en utilisant Coq) que Sq
est un transformateur de monade pour la monade (a -> q) -> a
.
Cependant, la monade (a -> q) -> a
a un autre transformateur de monade (m a -> q) -> m a
du type "composé à l'extérieur". Ceci est lié à la propriété de " rigidité " explorée dans la question Cette propriété d'un foncteur est-elle plus forte que celle d'une monade ? A savoir , (a -> q) -> a
est une monade rigide, et toutes les monades rigides ont des transformateurs de monade du type "composed-outside".
- En général, les monades transformées ne possèdent pas elles-mêmes automatiquement un transformateur de monade. C'est-à-dire qu'une fois que nous prenons une monade étrangère
m
et appliquer un transformateur de monade t
à celui-ci, nous obtenons une nouvelle monade t m
et cette monade n'a pas de transformateur : étant donné une nouvelle monade étrangère n
nous ne savons pas comment transformer n
avec la monade t m
. Si l'on connaît le transformateur mT
pour la monade m
on peut d'abord transformer n
con mT
et ensuite transformer le résultat avec t
. Mais si nous n'avons pas de transformateur pour la monade m
nous sommes bloqués : il n'y a pas de construction qui crée un transformateur pour la monade t m
à partir de la connaissance de t
seul et fonctionne pour les monades étrangères arbitraires m
.
Cependant, en pratique, toutes les monades explicitement définies ont des transformateurs explicitement définis, de sorte que ce problème ne se pose pas.
- La réponse de @JamesCandy suggère que pour toute monade (y compris
IO
? !), on peut écrire une expression de type (générale mais compliquée) qui représente le transformateur de monade correspondant. En d'autres termes, vous devez d'abord coder votre type de monade à l'aide de Church, ce qui fait ressembler le type à une monade de continuation, puis définir son transformateur de monade comme pour la monade de continuation. Mais je pense que c'est incorrect - cela ne donne pas une recette pour produire un transformateur de monade en général.
Prendre l'encodage Church d'un type a
signifie écrire le type
type ca = forall r. (a -> r) -> r
Ce type ca
est complètement isomorphe à a
par le lemme de Yoneda. Jusqu'à présent, nous n'avons rien obtenu d'autre que de rendre le type beaucoup plus compliqué en introduisant un paramètre de type quantifié. forall r
.
Encodons maintenant une monade de base par l'Église L
:
type CL a = forall r. (L a -> r) -> r
Encore une fois, nous n'avons rien obtenu jusqu'à présent, car CL a
est entièrement équivalent à L a
.
Maintenant, imaginez une seconde que CL a
une monade de continuation (ce qu'elle n'est pas !), et écrire le transformateur de monade comme s'il s'agissait d'un transformateur de monade de continuation, en remplaçant le type de résultat r
par le biais de m r
:
type TCL m a = forall r. (L a -> m r) -> m r
On prétend qu'il s'agit du "transformateur de monade codé par l'Eglise" pour L
. Mais cela semble être incorrect. Nous devons vérifier les propriétés :
-
TCL m
est une monade légale pour toute monade étrangère m
et pour toute monade de base L
-
m a -> TCL m a
est un morphisme monadique licite
La deuxième propriété est valable, mais je crois que la première ne l'est pas, - en d'autres termes, TCL m
n'est pas une monade pour une monade arbitraire m
. Peut-être que certaines monades m
l'admettent, mais d'autres ne le font pas. Je n'ai pas été capable de trouver une instance générale de monade pour TCL m
correspondant à une monade de base arbitraire L
.
Une autre façon d'argumenter que TCL m
n'est pas en général une monade est de noter que forall r. (a -> m r) -> m r
est en effet une monade pour tout constructeur de type m
. On désigne cette monade par CM
. Maintenant, TCL m a = CM (L a)
. Si TCL m
était une monade, cela impliquerait que CM
peut être composé avec n'importe quelle monade L
et donne une monade légale CM (L a)
. Cependant, il est très peu probable qu'une monade non triviale CM
(en particulier, une qui n'est pas équivalente à Reader
) se composera avec toutes les monades L
. Les monades ne se composent généralement pas sans contraintes supplémentaires strictes.
Un exemple spécifique où cela ne fonctionne pas est celui des monades de lecture. Considérons L a = r -> a
y m a = s -> a
donde r
y s
sont des types fixes. Maintenant, nous aimerions considérer le "transformateur de monades codées par l'Église". forall t. (L a -> m t) -> m t
. Nous pouvons simplifier cette expression de type en utilisant le lemme de Yoneda,
forall t. (x -> t) -> Q t = Q x
(pour tout foncteur Q
) et on obtient
forall t. (L a -> s -> t) -> s -> t
= forall t. ((L a, s) -> t) -> s -> t
= s -> (L a, s)
= s -> (r -> a, s)
C'est donc l'expression de type pour TCL m a
dans ce cas. Si TCL
était un transformateur de monades, alors P a = s -> (r -> a, s)
serait une monade. Mais on peut vérifier explicitement que cette P
n'est en fait pas une monade (on ne peut pas implémenter return
y bind
qui satisfont aux lois).
Même si cela fonctionnait (c'est-à-dire en supposant que j'ai fait une erreur en prétendant que TCL m
n'est en général pas une monade ), cette construction présente certains inconvénients :
- Il n'est pas functoriel (c'est-à-dire non covariant) par rapport à la monade étrangère
m
Nous ne pouvons donc pas interpréter une monade libre transformée en une autre monade, ni fusionner deux transformateurs de monades, comme expliqué ici. Existe-t-il un moyen fondé sur des principes de composer deux transformateurs de monade s'ils sont de type différent, mais que leur monade sous-jacente est du même type ?
- La présence d'un
forall r
rend le type assez compliqué à raisonner et peut entraîner une dégradation des performances (voir l'article "L'encodage Church considéré comme nuisible") et des débordements de pile (puisque l'encodage Church n'est généralement pas sûr pour la pile).
- Le transformateur de monade codé par Church pour une monade de base d'identité (
L = Id
) ne donne pas la monade étrangère non modifiée : T m a = forall r. (a -> m r) -> m r
et ce n'est pas la même chose que m a
. En fait, il est assez difficile de déterminer quelle est cette monade, étant donné une monade m
.
Un exemple qui montre pourquoi forall r
rend le raisonnement compliqué, considérons la monade étrangère m a = Maybe a
et essayer de comprendre ce que le type forall r. (a -> Maybe r) -> Maybe r
signifie en fait. Je n'ai pas été en mesure de simplifier ce type ou de trouver une bonne explication sur ce qu'il fait, c'est-à-dire quel type d'"effet" il représente (puisqu'il s'agit d'une monade, elle doit représenter un certain type d'"effet") et comment on pourrait utiliser un tel type.
- Le transformateur de monade codé par Church n'est pas équivalent aux transformateurs de monade standard bien connus tels que
ReaderT
, WriterT
, EitherT
, StateT
et ainsi de suite.
Il n'est pas clair combien d'autres transformateurs de monades existent et dans quels cas on utiliserait un transformateur ou un autre.
- L'une des questions du post est de trouver un exemple explicite d'une monade.
m
qui a deux transformateurs t1
y t2
de telle sorte que pour toute monade étrangère n
les monades t1 n
y t2 n
ne sont pas équivalentes.
Je crois que le Search
fournit un tel exemple.
type Search a = (a -> p) -> a
où p
est un type fixe.
Les transformateurs sont
type SearchT1 n a = (a -> n p) -> n a
type SearchT2 n a = (n a -> p) -> n a
J'ai vérifié que les deux SearchT1 n
y SearchT2 n
sont des monades licites pour toute monade n
. Nous avons des ascenseurs n a -> SearchT1 n a
y n a -> SearchT2 n a
qui fonctionnent en retournant des fonctions constantes (il suffit de retourner n a
tel que donné, en ignorant l'argument). Nous avons SearchT1 Identity
y SearchT2 Identity
évidemment équivalent à Search
.
La grande différence entre SearchT1
y SearchT2
c'est que SearchT1
n'est pas functoriel dans n
alors que SearchT2
est. Cela peut avoir des implications pour "l'exécution" ("l'interprétation") de la monade transformée, puisque normalement nous voudrions pouvoir lever un interpréteur n a -> n' a
en un "coureur" SearchT n a -> SearchT n' a
. Cela n'est possible qu'avec SearchT2
.
Une déficience similaire est présente dans les transformateurs de monades standard pour la monade de continuation et la monade de codensité : ils ne sont pas fonctoriels dans la monade étrangère.
5 votes
ST
est l'autre exemple évident, mais il viole également votre restriction de monade "pure".0 votes
Donc vous cherchez un type T tel qu'il y a un
instance Monad (T Identity)
qui satisfait aux lois des monades, mais tel que T ne satisfait pas aux lois des transformateurs de monades ?0 votes
@GaneshSittampalam Bon point, j'ai ajouté
ST
à la "liste d'exclusion".2 votes
@bennofs Oui, mais pas seulement les lois du transformateur de monade, ça pourrait être que
T
les satisfait, mais pour une certaine monaden
le typeT n
n'est pas une monade. Par exempleListT
ne satisfait pas aux lois des monades pour certaines monades seulement (non commutatives), cependant, il existe un autre transformateur correct pour[]
.4 votes
La liste des exceptions "c'est évidemment IO" est pratiquement infinie. Il y a
STM
par exemple. Mais il y a aussi toutes les monades personnalisées qui fonctionnent intrinsèquement sur IO. De nombreuses bibliothèques fournissent de telles choses.0 votes
Il est amusant de constater qu'il est très difficile de trouver un transformateur de monade trivial.
0 votes
Le "lecteur double duel"
(a -> b) -> b
pourrait être un candidat libre d'IO (a-t-il un nom standard / une implémentation, BTW) ?0 votes
@leftaroundabout
Cont
?1 votes
@Rhymoid
newtype Id1 m a = Id1 (m a)
?2 votes
@leftaroundabout
Cont
est presque le contraire : Il est si facile de se transformer en transformateur que les méthodes de laMonad
instance pourContT m
n'ont même pas besoin de laMonad
instance pourm
.2 votes
J'ai l'impression qu'une définition précise de ce que cela signifie pour un type de "posséder une monade" est nécessaire. Pourquoi est-ce que list "a" à la fois ListT et ListTDoneRight ? Peut-on répondre à cette question en faisant appel à des transformateurs de monades libres ? Quelque chose comme
T
conMonad T
tal queT' Identity ~ T
mais il existe unn
conMonad n
tal queT' n
n'instancie plusMonad
ou tel queT'
n'est pasMonadTrans
. Il pourrait y avoir beaucoup de telsT'
cependant (comme le souligne ListT). Tous sont pris en considération pour chaque projetT
mais0 votes
Cela ne semble pourtant pas évident, car le problème est facile à résoudre. Au moins la partie transformateur :
data I a = I () a
est une monade, est isomorphe àI' Identity
pourdata I' m a = I' (m ()) a
. MaisI'
n'est certainement pas un transformateur de monade.9 votes
Toute monade qui peut être exprimée comme une monade libre pour un certain foncteur (ce qui est le cas de presque toutes les monades) : stackoverflow.com/questions/14641864/ ) devrait également bénéficier du transformateur de monades gratuit.
1 votes
@Cirdec C'est un très bon point. Si une monade peut être construite comme une monade libre d'un certain foncteur, alors le même foncteur peut être utilisé pour construire son transformateur de monade correspondant. Il semble donc que les seuls candidats soient des monades qui ne peuvent pas être construites de cette façon.
0 votes
@PetrPudlák : non-constructif, mais puisque toutes les monades peuvent être écrites en termes de
Cont
et nous savons comment faireContT
toutes les monades ont un transformateur correspondant.0 votes
@JohnL Je pense que la manière simple d'intégrer une monade dans
Cont
est isomorphe à l'utilisation deContT
comme un transformateur, et il n'y a alors plus de place pour faire le résultat de cette transformation encore une autre monade.0 votes
@J.Abrahamson Ce n'est pas ce que je voulais dire.
Id1 Id
est isomorphe àId
seulement. Je pensais àTT m
un transformateur correspondant à toutm
(pour toutes les monadesm
:TT m Id ~ m
etc.).0 votes
@Rhymoid Je ne vois pas en quoi cela diffère :
Id1 m ~ m
pour toutes les monades. Si vous voulez insérer uneId
là-dedans, vous pouvez regardernewtype Compose f g a = Compose (f (g a))
?0 votes
@J.Abrahamson :
Id1 m ~ m
n'est pas suffisant pour queId1
oId1 m
un transformateur correspondant àm
selon la définition de l'OP.0 votes
@JohnL Cela semble prometteur, pourriez-vous nous en dire plus ? Par exemple, comment pourrait-on exprimer
[]
en utilisantCont
(pasContT
) ?0 votes
@PetrPudlák Quelque chose comme
\xs -> ContT (\f -> Identity (xs >>= runIdentity . f)
yrunIdentity . ($ Identity . return) . runContT
?0 votes
Le point de @Cirdec (que j'ai abordé de manière indépendante en un duplicata ) signifie qu'il y a effectivement sont transformateurs de monades correspondant à
IO
yST s
bien que la correspondance ne puisse pas être exprimée complètement en Haskell. Je ne sais pas si ces constructions ont une valeur pratique.0 votes
@PetrPudlák Je pense avoir un exemple de monade avec deux transformateurs non équivalents. Mon exemple est la "monade de recherche".
S p
défini partype S p a = (a -> p) -> a
a un transformateurt1 n a = (n a -> p) -> n a
et un transformateurt2 n a = (a -> n p) -> n a
. Les deux transformateurs ontlift
. Aussi,t1 Identity
est isomorphe àt2 Identity
et àS p
. Cependant,t2
n'est pas functoriel dans la monade étrangèren
alors quet1
est. Voir aussi le point 5 récemment ajouté dans ma réponse ci-dessous.