J'ai vu le terme Monade libre s'affiche tousmaintenantetalors pendant un certain temps, mais tout le monde semble simplement les utiliser/en discuter sans donner une explication de ce qu’ils sont. Alors: que sont les monades libres? (Je dirais que je connais les monades et les bases de Haskell, mais que je n'ai qu'une connaissance très approximative de la théorie des catégories.)
La réponse d'Edward Kmett est évidemment excellente. Mais c'est un peu technique. Voici une explication peut-être plus accessible.
Les monades gratuites ne sont qu'un moyen général de transformer des foncteurs en monades. C'est-à-dire, étant donné que n'importe quel foncteur f
Free f
est une monade. Ce ne serait pas très utile, sauf que vous obtenez une paire de fonctions
liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r
le premier vous permet "d'entrer" dans votre monade et le second vous donne le moyen de "s'en sortir".
Plus généralement, si X est un Y avec des éléments supplémentaires P, un "X libre" est un moyen de passer d'un Y à un X sans rien gagner de plus.
Exemples: un monoïde (X) est un ensemble (Y) avec une structure supplémentaire (P) qui indique fondamentalement qu'il a une opération (vous pouvez penser à l'addition) et une certaine identité (comme zéro).
Alors
class Monoid m where
mempty :: m
mappend :: m -> m -> m
Maintenant, nous connaissons tous les listes
data [a] = [] | a : [a]
Bien, quel que soit le type t
on sait que [t]
est un monoïde
instance Monoid [t] where
mempty = []
mappend = (++)
et ainsi les listes sont le "monoïde libre" sur les ensembles (ou dans les types Haskell).
Ok, les monades libres sont la même idée. Nous prenons un foncteur et rendons une monade. En fait, les monades pouvant être considérées comme des monoïdes dans la catégorie des endofoncteurs, la définition d'une liste
data [a] = [] | a : [a]
ressemble beaucoup à la définition des monades libres
data Free f a = Pure a | Roll (f (Free f a))
et l'instance Monad
a une similitude avec l'instance Monoid
pour les listes
--it needs to be a functor
instance Functor f => Functor (Free f) where
fmap f (Pure a) = Pure (f a)
fmap f (Roll x) = Roll (fmap (fmap f) x)
--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)
instance Functor f => Monad (Free f) where
return = Pure -- just like []
x >>= f = concatFree (fmap f x) --this is the standard concatMap definition of bind
maintenant, nous obtenons nos deux opérations
-- this is essentially the same as \x -> [x]
liftFree :: Functor f => f a -> Free f a
liftFree x = Roll (fmap Pure x)
-- this is essentially the same as folding a list
foldFree :: Functor f => (f r -> r) -> Free f r -> r
foldFree _ (Pure a) = a
foldFree f (Roll x) = f (fmap (foldFree f) x)
Voici une réponse encore plus simple: un monade est quelque chose qui "calcule" lorsque le contexte monadique est réduit par join :: m (m a) -> m a
(rappelant que >>=
peut être défini comme x >>= y = join (fmap y x)
). C'est ainsi que les monades portent le contexte à travers une chaîne séquentielle de calculs: parce qu'à chaque point de la série, le contexte de l'appel précédent est réduit au suivant.
Un monade libre satisfait toutes les lois de la Monade, mais ne fait aucune réduction (c'est-à-dire le calcul). Cela ne fait que créer une série imbriquée de contextes. L'utilisateur qui crée une telle valeur monadique libre est responsable de l'utilisation de ces contextes imbriqués, de sorte que le signification d'une telle composition puisse être différé jusqu'à ce que la valeur monadique ait été créée.
Un foo gratuit s'avère être la chose la plus simple qui satisfait toutes les lois du "foo". C'est-à-dire qu'il répond exactement aux lois nécessaires pour être un foo et rien de plus.
Un foncteur oublieux est celui qui "oublie" une partie de la structure qui passe d'une catégorie à une autre.
Les foncteurs donnés F : D -> C
, et G : C -> D
, nous disons F -| G
, F
est adjoint à gauche de G
, ou G
est droit adjoint à F
à chaque fois que tout a, b: F a -> b
est isomorphe à a -> G b
, où les flèches proviennent des catégories appropriées.
Formellement, un foncteur gratuit est adjoint à un foncteur oublieux.
Le monoïde libre
Commençons par un exemple plus simple, le monoïde libre.
Prenez un monoïde défini par un ensemble de transporteurs T
, une fonction binaire pour écraser une paire d’éléments ensemble f :: T → T → T
et un unit :: T
de sorte que vous ayez une loi associative, et une loi sur l'identité: f(unit,x) = x = f(x,unit)
.
Vous pouvez créer un foncteur U
à partir de la catégorie des monoïdes (où les flèches sont des homomorphismes de monoïdes, c’est-à-dire qu’elles assurent la correspondance de unit
à unit
sur l’autre monoïde et que vous pouvez composer avant ou après avoir mappé sur l'autre monoïde sans changer de sens) vers la catégorie des ensembles (où les flèches sont simplement des flèches de fonction) qui "oublie" l'opération et unit
, et vous donne simplement le jeu d'opérateurs.
Ensuite, vous pouvez définir un foncteur F
de la catégorie des ensembles à la catégorie des monoïdes laissée adjacente à ce foncteur. Ce foncteur est le foncteur qui mappe un ensemble a
sur le monoïde [a]
, où unit = []
et mappend = (++)
.
Donc, pour passer en revue notre exemple jusqu'à présent, en pseudo-Haskell:
U : Mon → Set -- is our forgetful functor
U (a,mappend,mempty) = a
F : Set → Mon -- is our free functor
F a = ([a],(++),[])
Ensuite, pour montrer que F
est libre, vous devez démontrer qu’il est adjoint à U
, un foncteur oublieux, c’est-à-dire que, comme nous l’avons mentionné plus haut, nous devons montrer que
F a → b
est isomorphe à a → U b
maintenant, rappelez-vous que la cible de F
appartient à la catégorie Mon
des monoïdes, où les flèches sont des homomorphismes monoïdes. Nous avons donc besoin de montrer qu'un homomorphisme monoïde de [a] → b
peut être décrit précisément par une fonction de a → b
.
En Haskell, nous appelons le côté de ce qui vit dans Set
(heu, Hask
, la catégorie des types de Haskell que nous prétendons être définie), juste foldMap
, qui, lorsqu'il est spécialisé à partir de Data.Foldable
to Lists est de type Monoid m => (a → m) → [a] → m
.
Il s’agit d’une adjonction qui a des conséquences. Notamment, si vous oubliez puis construisez avec free, puis de nouveau, c’est comme si vous aviez oublié une fois, et nous pouvons utiliser cela pour créer la jointure monadique. puisque UFUF
~ U(FUF)
~ UF
, et que nous pouvons transmettre l'homomorphisme monoïde d'identité de [a]
à [a]
à travers l'isomorphisme qui définit notre adjonction, obtenir qu'une liste isomorphisme de [a] → [a]
est une fonction de type a -> [a]
et ne renvoie que les listes.
Vous pouvez composer tout cela plus directement en décrivant une liste en ces termes avec:
newtype List a = List (forall b. Monoid b => (a -> b) -> b)
La Monade Libre
Alors, quel est un Monade libre ?
Eh bien, nous faisons la même chose que nous faisions avant, nous commençons par un foncteur oublieux U de la catégorie des monades où les flèches sont homomorphismes de monades à une catégorie d’endofoncteurs où les flèches sont des transformations naturelles, et nous cherchons un foncteur qui reste à gauche pour que.
Alors, comment cela se rapporte-t-il à la notion de monade libre telle qu’elle est habituellement utilisée?
Savoir que quelque chose est une monade libre, Free f
, vous indique que donner un homomorphisme de monade à partir de Free f -> m
revient à (isomorphe à) de donner une transformation naturelle (homomorphisme de foncteur) à partir de f -> m
. Rappelez-vous que F a -> b
doit être isomorphe à a -> U b
pour que F soit laissé à côté de U. U mappe ici les monades sur les foncteurs.
F est au moins isomorphe au type Free
que j'utilise dans mon package free
sur le hackage.
Nous pourrions également le construire en analogie plus étroite avec le code ci-dessus pour la liste libre, en définissant
class Algebra f x where
phi :: f x -> x
newtype Free f a = Free (forall x. Algebra f x => (a -> x) -> x)
Cofree Comonads
Nous pouvons construire quelque chose de similaire en regardant l'adjoint de droite d'un foncteur oublieux en supposant qu'il existe. Un foncteur cofree est simplement/juste adjoint/à un foncteur oublieux, et par symétrie, savoir que quelque chose est un cofree comonad revient à savoir que donner un homomorphisme de comonad de w -> Cofree f
revient à donner une transformation naturelle de w -> f
.
La monade libre (structure de données) appartient à la monade (classe), tout comme la liste (structure de données) au monoïde (classe): il s'agit d'une implémentation triviale, dans laquelle vous pouvez décider ensuite de la manière dont le contenu sera combiné.
Vous savez probablement ce qu'est une monade et que chaque monade nécessite une implémentation spécifique (respectueuse des lois de la monade) de fmap
+ join
+ return
ou bind
+ return
.
Supposons que vous ayez un Functor (une implémentation de fmap
), mais le reste dépend des valeurs et des choix effectués à l'exécution, ce qui signifie que vous voulez pouvoir utiliser les propriétés Monad, mais que vous souhaitez ensuite choisir les fonctions Monad.
Cela peut être fait en utilisant la Monade Libre (structure de données), qui enveloppe le Functor (type) de telle sorte que le join
soit plutôt un empilement de ces foncteurs qu'une réduction.
Les vrais return
et join
que vous voulez utiliser peuvent maintenant être donnés comme paramètres de la fonction de réduction foldFree
:
foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree return join :: Monad m => Free m a -> m a
Pour expliquer les types, nous pouvons remplacer Functor f
par Monad m
et b
par (m a)
:
foldFree :: Monad m => (a -> (m a)) -> (m (m a) -> (m a)) -> Free m a -> (m a)
Une monade libre de Haskell est une liste de foncteurs. Comparer:
data List a = Nil | Cons a (List a )
data Free f r = Pure r | Free (f (Free f r))
Pure
est analogue à Nil
et Free
est analogue à Cons
. Une monade libre stocke une liste de foncteurs au lieu d'une liste de valeurs. Techniquement, vous pouvez implémenter des monades libres en utilisant un type de données différent, mais toute implémentation doit être isomorphe à celle ci-dessus.
Vous utilisez des monades gratuites chaque fois que vous avez besoin d'un arbre de syntaxe abstraite. Le foncteur de base de la monade libre est la forme de chaque étape de l’arbre de syntaxe.
My post , que quelqu'un a déjà lié, donne plusieurs exemples sur la façon de construire des arbres de syntaxe abstraits avec des monades libres
Je pense qu'un exemple concret simple aidera. Supposons que nous ayons un foncteur
data F a = One a | Two a a | Two' a a | Three Int a a a
avec l'évident fmap
. Alors Free F a
est le type d’arbres dont les feuilles ont le type a
et dont les nœuds sont marqués avec One
, Two
, Two'
et Three
. Les nœuds One
- ont un enfant, les nœuds Two
- et Two'
- ont deux enfants et les nœuds Three
- possèdent également un tag Int
.
Free F
est une monade. return
mappe x
sur l'arborescence qui n'est qu'une feuille de valeur x
. t >>= f
regarde chacune des feuilles et les remplace par des arbres. Lorsque la feuille a la valeur y
, elle est remplacée par l’arbre f y
.
Un diagramme rend cela plus clair, mais je n’ai pas les moyens de le dessiner facilement!