Tout en expliquant à quelqu'un ce qu'est une classe de type X, j'ai du mal à trouver de bons exemples de structures de données qui sont exactement X.
Je demande donc des exemples pour:
- Un constructeur de type qui n'est pas un Functor.
- Un constructeur de type qui est un Functor, mais pas Applicative.
- Un constructeur de type qui est un Applicatif, mais qui n'est pas une Monade.
- Un constructeur de type qui est une Monade.
Je pense qu'il y a beaucoup d'exemples de Monade partout, mais un bon exemple de Monade avec une relation avec les exemples précédents pourrait compléter l'image.
Je cherche des exemples qui seraient similaires les uns aux autres, ne différant que par des aspects importants pour l'appartenance à la classe de type particulière.
Si on arrivait à trouver un exemple d'Arrow quelque part dans cette hiérarchie (est-ce entre Applicative et Monad?), Ce serait bien aussi!
haskell
monads
functor
applicative
Rotsor
la source
la source
* -> *
) pour lequel il n'existe pas de convenablefmap
?a -> String
n'est pas un foncteur.a -> String
est un foncteur mathématique, mais pas un HaskellFunctor
, pour être clair.(a -> b) -> f b -> f a
Réponses:
Un constructeur de type qui n'est pas un Functor:
Vous pouvez en faire un foncteur contravariant, mais pas un foncteur (covariant). Essayez d'écrire
fmap
et vous échouerez. Notez que la version du foncteur contravariant est inversée:Un constructeur de type qui est un foncteur, mais pas Applicatif:
Je n'ai pas de bon exemple. Il y en a
Const
, mais idéalement j'aimerais un béton non monoïde et je ne pense à aucun. Tous les types sont essentiellement numériques, des énumérations, des produits, des sommes ou des fonctions lorsque vous y arrivez. Vous pouvez voir ci-dessous pigworker et je ne suis pas d'accord pour savoir siData.Void
c'est unMonoid
;Puisqu'il
_|_
s'agit d'une valeur légale dans Haskell, et en fait la seule valeur légale deData.Void
, cela répond aux règles monoïdes. Je ne sais pas quoiunsafeCoerce
en faire, car votre programme n'est plus garanti de ne pas violer la sémantique de Haskell dès que vous utilisez uneunsafe
fonction.Voir le wiki de Haskell pour un article sur les fonctions inférieures ( lien ) ou dangereuses ( lien ).
Je me demande s'il est possible de créer un tel constructeur de type en utilisant un système de type plus riche, comme Agda ou Haskell avec différentes extensions.
Un constructeur de type qui est un Applicatif, mais pas une Monade:
Vous pouvez en faire un applicatif, avec quelque chose comme:
Mais si vous en faites une monade, vous pourriez obtenir un décalage de dimension. Je soupçonne que des exemples comme celui-ci sont rares dans la pratique.
Un constructeur de type qui est une Monade:
À propos des flèches:
Demander où se trouve une flèche sur cette hiérarchie, c'est comme demander quel genre de forme "rouge" est. Notez le genre de décalage:
mais,
la source
Either a
exemple pour le dernier cas, car c'est plus facile à comprendre.ZipList
._|_
habite chaque type dans *, mais le faitVoid
est que vous devriez avoir à vous pencher en arrière pour en construire un ou vous avez détruit sa valeur. C'est pourquoi ce n'est pas une instance de Enum, Monoid, etc. Si vous en avez déjà un, je suis heureux de vous laisser les écraser ensemble (en vous donnant unSemigroup
) maismempty
, mais je ne donne aucun outil pour construire explicitement une valeur de typeVoid
dansvoid
. Vous devez charger le pistolet et le pointer vers votre pied et appuyer sur la gâchette vous-même.Mon style peut être à l'étroit par mon téléphone, mais voilà.
ne peut pas être Functor. Si c'était le cas, nous aurions
et la Lune serait faite de fromage vert.
pendant ce temps
est un foncteur
mais ne peut pas être d'application, ou nous aurions
et le vert serait fait de fromage Moon (ce qui peut effectivement arriver, mais seulement plus tard dans la soirée).
(Remarque supplémentaire:,
Void
comme dansData.Void
est un type de données vide. Si vous essayez d'utiliserundefined
pour prouver que c'est un monoïde, je vais utiliserunsafeCoerce
pour prouver que ce n'est pas le cas.)Joyeusement,
est applicable à bien des égards, par exemple, comme le voudrait Dijkstra,
mais ça ne peut pas être une Monade. Pour voir pourquoi, observez que le retour doit être constant
Boo True
ouBoo False
, et donc quene peut pas tenir.
Oh ouais, j'ai presque oublié
est une monade. Roulez le vôtre.
Avion pour attraper ...
la source
mempty
._|_
Je crois que les autres réponses ont manqué quelques exemples simples et courants:
Un constructeur de type qui est un foncteur mais pas un applicatif. Un exemple simple est une paire:
Mais il n'y a aucun moyen de définir son
Applicative
instance sans imposer de restrictions supplémentairesr
. En particulier, il n'y a aucun moyen de définirpure :: a -> (r, a)
un arbitrairer
.Un constructeur de type qui est un Applicatif, mais qui n'est pas une Monade. Un exemple bien connu est ZipList . (C'est un
newtype
qui encapsule les listes et leur fournit uneApplicative
instance différente .)fmap
est défini de la manière habituelle. Maispure
et<*>
sont définis commepure
crée ainsi une liste infinie en répétant la valeur donnée, et<*>
zippe une liste de fonctions avec une liste de valeurs - applique la i- ème fonction au i- ème élément. (La norme<*>
sur[]
produit toutes les combinaisons possibles d'application de la fonction i -th à l' élément j -th.) Mais il n'y a aucun moyen sensé de définir une monade (voir cet article ).Comment les flèches s'intègrent-elles dans la hiérarchie foncteur / applicative / monade? Voir Les idiomes sont inconscients, les flèches sont méticuleuses, les monades sont promiscueuses par Sam Lindley, Philip Wadler, Jeremy Yallop. MSFP 2008. (Ils appellent les idiomes des foncteurs applicatifs .) Le résumé:
la source
((,) r)
va de même d'un foncteur qui n'est pas un applicatif; mais c'est uniquement parce que vous ne pouvez généralement pas définirpure
pour tousr
à la fois. C'est donc une bizarrerie de concision du langage, d'essayer de définir une collection (infinie) de foncteurs applicatifs avec une définition depure
et<*>
; en ce sens, il ne semble rien y avoir de mathématiquement profond dans ce contre-exemple puisque, pour tout bétonr
,((,) r)
on peut faire un foncteur applicatif. Question: Pouvez-vous penser à un foncteur BÉTON qui ne soit pas applicatif?Un bon exemple pour un constructeur de type qui n'est pas un foncteur est
Set
: Vous ne pouvez pas implémenterfmap :: (a -> b) -> f a -> f b
, car sans contrainte supplémentaireOrd b
vous ne pouvez pas construiref b
.la source
fmap
est juste un problème de langue / implémentation. En outre, il est possible d'envelopperSet
la monade de continuation, ce qui en fait une monade avec toutes les propriétés que nous attendons, voir cette question (bien que je ne sois pas sûr que cela puisse être fait efficacement).b
peut être indécidable, dans ce cas, vous ne pouvez pas définirfmap
!Functor
instance avec laOrd
contrainte, mais cela pourrait être possible avec une définition différenteFunctor
ou une meilleure prise en charge du langage. En fait, avec ConstraintKinds, il est possible de définir une classe de type qui peut être paramétrée comme ceci.ord
contrainte, le fait qu'unSet
ne puisse pas contenir des entrées en double signifie que celafmap
pourrait altérer le contexte. Cela viole la loi d'associativité.Je voudrais proposer une approche plus systématique pour répondre à cette question, et aussi pour montrer des exemples qui n'utilisent aucune astuce spéciale comme les valeurs "inférieures" ou les types de données infinis ou quelque chose comme ça.
Quand les constructeurs de types ne parviennent-ils pas à avoir des instances de classe de type?
En général, il existe deux raisons pour lesquelles un constructeur de type ne parvient pas à avoir une instance d'une certaine classe de type:
Les exemples du premier type sont plus faciles que ceux du deuxième type car pour le premier type, il suffit de vérifier si l'on peut implémenter une fonction avec une signature de type donnée, tandis que pour le second type, nous devons prouver qu'aucune implémentation pourrait éventuellement satisfaire aux lois.
Exemples spécifiques
Un constructeur de type qui ne peut pas avoir d'instance de foncteur car le type ne peut pas être implémenté:
Il s'agit d'un contrafoncteur, pas d'un foncteur, par rapport au paramètre type
a
, cara
dans une position contravariante. Il est impossible d'implémenter une fonction avec signature de type(a -> b) -> F z a -> F z b
.Un constructeur de type qui n'est pas un foncteur légal même si la signature de type de
fmap
peut être implémentée:L'aspect curieux de cet exemple est que nous pouvons implémenter
fmap
le bon type même s'ilF
ne peut pas être un foncteur car il utilisea
dans une position contravariante. Donc, cette implémentation de cellefmap
montrée ci-dessus est trompeuse - même si elle a la signature de type correcte (je crois que c'est la seule implémentation possible de cette signature de type), les lois du foncteur ne sont pas satisfaites. Par exemple,fmap id
≠id
, parce quelet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
est123
, maislet (Q(f,_)) = id (Q(read,"123")) in f "456"
est456
.En fait,
F
c'est seulement un profuncteur, - ce n'est ni un foncteur ni un contrefoncteur.Un foncteur légal qui n'est pas applicatif car la signature de type de
pure
ne peut pas être implémentée: prenez la monade Writer(a, w)
et supprimez la contrainte quiw
devrait être un monoïde. Il est alors impossible de construire une valeur de type(a, w)
sura
.Un foncteur qui n'est pas applicative car la signature de type de
<*>
ne peut pas être mis en œuvre:data F a = Either (Int -> a) (String -> a)
.Un foncteur qui n'est pas d'application légale même si les méthodes de classe de type peuvent être implémentées:
Le constructeur de type
P
est un foncteur car il n'utilisea
que dans des positions covariantes.La seule implémentation possible de la signature de type de
<*>
est une fonction qui renvoie toujoursNothing
:Mais cette implémentation ne satisfait pas à la loi d'identité des foncteurs applicatifs.
Applicative
pas unMonad
car la signature de type debind
ne peut pas être implémentée.Je ne connais pas de tels exemples!
Applicative
pas unMonad
car les lois ne peuvent pas être satisfaites même si la signature de type debind
peut être implémentée.Cet exemple a généré pas mal de discussions, il est donc sûr de dire que prouver cet exemple n'est pas facile. Mais plusieurs personnes l'ont vérifié indépendamment par différentes méthodes. Voir Is `data PoE a = Empty | Paire aa` une monade? pour une discussion supplémentaire.
Il est quelque peu lourd de prouver qu'il n'y a pas d'
Monad
instance légale . La raison du comportement non monadique est qu'il n'y a aucun moyen naturel d'implémenterbind
quand une fonctionf :: a -> B b
pourrait retournerNothing
ouJust
pour différentes valeurs dea
.Il est peut-être plus clair d'envisager
Maybe (a, a, a)
, qui n'est pas non plus une monade, et d'essayer de l'implémenterjoin
pour cela. On constatera qu'il n'y a pas de moyen de mise en œuvre intuitivement raisonnablejoin
.Dans les cas indiqués par
???
, il semble clair que nous ne pouvons pas produireJust (z1, z2, z3)
de manière raisonnable et symétrique sur six valeurs de type différentesa
. Nous pourrions certainement choisir un sous-ensemble arbitraire de ces six valeurs, - par exemple, toujours prendre la première non videMaybe
- mais cela ne satisferait pas les lois de la monade. Le retourNothing
ne satisfera pas non plus aux lois.bind
- mais qui ne respecte pas les lois sur l'identité.La monade arborescente habituelle (ou "un arbre avec des branches en forme de foncteur") est définie comme
Il s'agit d'une monade gratuite sur le foncteur
f
. La forme des données est un arbre où chaque point de branchement est un "foncteur" de sous-arbres. L'arbre binaire standard serait obtenu avectype f a = (a, a)
.Si nous modifions cette structure de données en faisant également les feuilles en forme de foncteur
f
, nous obtenons ce que j'appelle une "semi-monade" - elle abind
qui satisfait aux lois de naturalité et d'associativité, mais sapure
méthode échoue à l'une des lois d'identité. "Les semi-monades sont des semi-groupes dans la catégorie des endofuncteurs, quel est le problème?" Ceci est la classe de typeBind
.Par souci de simplicité, je définis la
join
méthode au lieu debind
:La greffe de branche est standard, mais la greffe de feuille est non standard et produit a
Branch
. Ce n'est pas un problème pour la loi d'associativité mais viole l'une des lois d'identité.Quand les types polynomiaux ont-ils des instances de monade?
Aucun des foncteurs
Maybe (a, a)
etMaybe (a, a, a)
ne peut se voir attribuer uneMonad
instance légale , bien qu'ils le soient évidemmentApplicative
.Ces foncteurs n'ont aucune astuce - pas
Void
oubottom
n'importe où, pas de paresse / rigueur délicate, pas de structures infinies et pas de contraintes de classe de type. L'Applicative
instance est complètement standard. Les fonctionsreturn
etbind
peuvent être implémentées pour ces foncteurs mais ne satisferont pas aux lois de la monade. En d'autres termes, ces foncteurs ne sont pas des monades car il manque une structure spécifique (mais il n'est pas facile de comprendre ce qui manque exactement). Par exemple, un petit changement dans le foncteur peut en faire une monade:data Maybe a = Nothing | Just a
c'est une monade. Un autre foncteur similairedata P12 a = Either a (a, a)
est également une monade.Constructions pour les monades polynomiales
En général, voici quelques constructions qui produisent des
Monad
s licites à partir de types polynomiaux. Dans toutes ces constructions,M
est une monade:type M a = Either c (w, a)
oùw
est monoïdetype M a = m (Either c (w, a))
oùm
est toute monade etw
tout monoïdetype M a = (m1 a, m2 a)
oùm1
et oùm2
sont les monadestype M a = Either a (m a)
oùm
est toute monadeLa première construction est
WriterT w (Either c)
, la deuxième construction estWriterT w (EitherT c m)
. La troisième construction est un produit de monadespure @M
par composant : est définie comme le produit par composant depure @m1
etpure @m2
, etjoin @M
est définie en omettant les données de produits croisés (par exemple,m1 (m1 a, m2 a)
est mappéem1 (m1 a)
en omettant la deuxième partie du tuple):La quatrième construction est définie comme
J'ai vérifié que les quatre constructions produisent des monades légales.
Je suppose qu'il n'y a pas d'autres constructions pour les monades polynomiales. Par exemple, le foncteur
Maybe (Either (a, a) (a, a, a, a))
n'est obtenu par aucune de ces constructions et n'est donc pas monadique. Cependant,Either (a, a) (a, a, a)
est monadique parce qu'il est isomorphe au produit de trois monadesa
,a
, etMaybe a
. Aussi,Either (a,a) (a,a,a,a)
est monadique car il est isomorphe au produit dea
etEither a (a, a, a)
.Les quatre constructions présentées ci-dessus nous permettront d'obtenir n'importe quelle somme de n'importe quel nombre de produits de n'importe quel nombre
a
, par exempleEither (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
et ainsi de suite. Tous ces constructeurs de types auront (au moins une)Monad
instance.Il reste à voir, bien sûr, quels cas d'utilisation pourraient exister pour de telles monades. Un autre problème est que les
Monad
instances dérivées via les constructions 1-4 ne sont généralement pas uniques. Par exemple, le constructeur de typetype F a = Either a (a, a)
peut recevoir uneMonad
instance de deux manières: par la construction 4 en utilisant la monade(a, a)
, et par la construction 3 en utilisant l'isomorphisme de typeEither a (a, a) = (a, Maybe a)
. Encore une fois, trouver des cas d'utilisation pour ces implémentations n'est pas immédiatement évident.Une question demeure - étant donné un type de données polynomiales arbitraires, comment savoir s'il a une
Monad
instance. Je ne sais pas prouver qu'il n'y a pas d'autres constructions pour les monades polynomiales. Je ne pense pas qu'il existe jusqu'à présent de théorie pour répondre à cette question.la source
B
est une monade. Pouvez-vous donner un contre-exemple à cette liaisonPair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty
?f
tel quif x
estEmpty
maisf y
est unPair
, et à l'étape suivante, les deux le sontPair
. J'ai vérifié à la main que les lois ne s'appliquent pas à cette implémentation ou à toute autre implémentation. Mais c'est assez de travail pour le faire. Je souhaite qu'il y ait un moyen plus simple de comprendre cela!Maybe
carMaybe
il ne contient pas de valeurs différentes donta
s'inquiéter.Monad
instance se compose de fonctionsreturn
etbind
qui satisfont aux lois. Il existe deux implémentations dereturn
et 25 implémentationsbind
qui correspondent aux types requis. Vous pouvez montrer par calcul direct qu'aucune des implémentations ne satisfait aux lois. Pour réduire la quantité de travail requis, j'ai utiliséjoin
au lieu debind
et utilisé les lois d'identité en premier. Mais ça a été pas mal de travail.Traversable
soit nécessaire.m (Either a (m a))
est transformé en utilisantpure @m
enm (Either (m a) (m a))
. Puis trivialementEither (m a) (m a) -> m a
, et nous pouvons utiliserjoin @m
. C'est la mise en œuvre pour laquelle j'ai vérifié les lois.