luqui la réponse est excellente, mais je vais vous donner une autre explication de l' forall b. (a -> b) -> b === a
pour un couple de raisons: d'Abord, parce que je pense que la généralisation de Codensity est un peu overenthusiastic. Et deuxièmement, parce que c'est une occasion de lier un tas de choses intéressantes ensemble. À partir de!
z5h de la Boîte Magique
Imaginez que quelqu'un a renversé une pièce de monnaie puis le mettre dans une boîte magique. Vous ne pouvez pas voir à l'intérieur de la boîte, mais si vous choisissez un type b
et passe de la boîte une fonction du type Bool -> b
, la boîte va cracher un b
. Que pouvons-nous apprendre au sujet de cette zone, sans regarder à l'intérieur? Peut-on savoir quel est l'état de la pièce est? Pouvons-nous apprendre de ce mécanisme de la boîte utilise pour produire de l' b
? Comme il s'avère, nous pouvons faire les deux.
Nous pouvons définir la zone de rang 2 et la fonction de type Box Bool
où
type Box a = forall b. (a -> b) -> b
(Ici, le rang 2 de type signifie que la case maker choisit a
, et la boîte utilisateur choisit b
.)
Nous avons mis l' a
dans la zone et puis on ferme la boîte, de la création... d'une fermeture.
-- Put the a in the box.
box :: a -> Box a
box a f = f a
Par exemple, box True
. L'application partielle est juste un moyen astucieux pour créer des fermetures!
Maintenant, c'est la pièce à pile ou face? Depuis que j', la boîte de l'utilisateur, je suis autorisé à choisir l' b
, je peux choisir Bool
et passer dans une fonction, Bool -> Bool
. Si je décide id :: Bool -> Bool
alors la question est: est-ce la boîte de cracher sur la valeur qu'elle contient? La réponse est que la boîte soit cracher à la valeur qu'il contient ou qu'il va cracher non-sens (d'une valeur inférieure comme undefined
). En d'autres termes, si vous obtenez une réponse alors que la réponse doit être correcte.
-- Get the a out of the box.
unbox :: Box a -> a
unbox f = f id
Parce que nous ne pouvons pas générer des valeurs arbitraires en Haskell, la seule chose que le sens de la boîte peut faire, c'est d'appliquer la fonction donnée de la valeur, il se cache. C'est une conséquence de polymorphisme paramétrique, aussi connu comme parametricity.
Maintenant, pour montrer qu' Box a
est isomorphe a' a
, nous avons besoin de prouver deux choses à propos du boxing et unboxing. Nous devons prouver que vous obtenez ce que vous mettez dans et que vous pouvez mettre dans ce que vous sortez.
unbox . box = id
box . unbox = id
Je vais le faire à la première, et laisser la seconde comme un exercice pour le lecteur.
unbox . box
= {- definition of (.) -}
\b -> unbox (box b)
= {- definition of unbox and (f a) b = f a b -}
\b -> box b id
= {- definition of box -}
\b -> id b
= {- definition of id -}
\b -> b
= {- definition of id, backwards -}
id
(Si ces preuves semblent plutôt trivial, c'est parce que tous (total) fonctions polymorphes en Haskell sont les transformations naturelles et de ce que nous sommes en train de prouver ici est de naturalité. Parametricity encore une fois, nous offre, avec les théorèmes de bas, à bas prix!)
Comme d'un côté et d'un autre exercice pour le lecteur, pourquoi je ne peux pas définir réellement rebox
avec (.)
?
rebox = box . unbox
Pourquoi dois-je incorporer la définition de l' (.)
moi-même, comme une sorte de caverne de la personne?
rebox :: Box a -> Box a
rebox f = box (unbox f)
(Astuce: quels sont les types d' box
, unbox
, et (.)
?)
Identité et Codensity et de Yoneda, Oh Mon dieu!
Maintenant, comment peut-on généraliser Box
? luqui utilise Codensity: les deux b
s sont généralisées par l'arbitraire d'un constructeur de type que nous appellerons f
. C'est le Codensity de transformation de l' f a
.
type CodenseBox f a = forall b. (a -> f b) -> f b
Si nous fixons f ~ Identity
puis nous revenons Box
. Cependant, il existe une autre option: on peut frapper seulement le type de retour avec f
:
type YonedaBox f a = forall b. (a -> b) -> f b
(J'ai sorte de donné du jeu ici avec ce nom, mais nous allons y revenir.) Nous pouvons également fixer f ~ Identity
ici pour récupérer Box
, mais nous laissez la boîte de l'utilisateur de passer dans une fonction normale plutôt qu'une Kleisli flèche. Pour comprendre ce que nous sommes généraliser, revenons sur la définition de l' box
:
box a f = f a
Eh bien, c'est juste flip ($)
, n'est-ce pas? Et il s'avère que nos deux autres cases sont construites par la généralisation ($)
: CodenseBox
est appliquée partiellement, renversé monadique lier et YonedaBox
est appliquée partiellement flip fmap
. (Cela explique aussi pourquoi, Codensity f
est Monad
et Yoneda f
est Functor
pour tout choix d' f
: La seule façon de créer l'on est par la fermeture de plus d'un bind ou fmap, respectivement.) En outre, ces deux ésotérique de la catégorie de la théorie des concepts sont vraiment des généralisations d'un concept qui est familier à beaucoup de travail programmeurs: le CPS transformer!
En d'autres termes, YonedaBox
est le Yoneda d'Incorporation et de l'correctement captée box
/unbox
lois pour YonedaBox
sont la preuve du Lemme de Yoneda!
TL;DR:
forall b. (a -> b) -> b === a
est une instance de la Lemme de Yoneda.