Quel est le but de Rank2Types?
Je ne suis pas vraiment compétent dans Haskell, donc cela pourrait être une question facile.
Quelle langue limitation ne Rank2Types résoudre? Ne pas les fonctions en Haskell déjà soutien polymorphes arguments?
Vous devez vous connecter pour publier un commentaire.
Ils le font, mais seulement de rang 1. Cela signifie que si vous pouvez écrire une fonction qui prend différents types d'arguments sans cette extension, vous ne pouvez pas écrire une fonction qui utilise son argument que les différents types de la même invocation.
Par exemple, la fonction suivante peut pas être tapé sans cette extension, car
g
est utilisé avec différents types d'arguments dans la définition def
:Noter qu'il est parfaitement possible de passer d'une fonction polymorphe comme argument d'une autre fonction. Donc, quelque chose comme
map id ["a","b","c"]
est parfaitement légal. Mais la fonction ne peut l'utiliser comme monomorphes. Dans l'exemplemap
utiliseid
comme si elle avait le typeString -> String
. Et bien sûr, vous pouvez également passer une simple monomorphe fonction du type donné au lieu deid
. Sans rank2types il n'existe aucun moyen pour une fonction d'exiger que son argument doit être une fonction polymorphe et donc aucun moyen de l'utiliser comme une fonction polymorphe.f' g x y = g x + g y
. Ses déduit de rang 1 est de typeforall a r. Num r => (a -> r) -> a -> a -> r
. Depuisforall a
est en dehors de la fonction flèches, l'appelant doit d'abord choisir un type dea
; si elles choisissentInt
, nous obtenonsf' :: forall r. Num r => (Int -> r) -> Int -> Int -> r
, et maintenant nous avons fixé leg
argument de sorte qu'il peut prendreInt
mais pasString
. Si nous permettons àRankNTypes
nous pouvons annoterf'
avec le typeforall b c r. Num r => (forall a. a -> r) -> b -> c -> r
. Ne pouvez pas l'utiliser, bien que—ce qui seraitg
être?Il est difficile de comprendre de plus haut rang polymorphisme, à moins que vous étude Système De F directement, parce que Haskell est conçu pour cacher les détails de qui de vous, dans l'intérêt de la simplicité.
Mais, fondamentalement, l'idée est que les types polymorphes n'ont pas vraiment de la
a -> b
forme qu'ils n'en Haskell; en réalité, ils ressemblent à de ce, toujours de manière explicite les quantificateurs:Si vous ne connaissez pas le "∀" symbole, c'est de lire que "pour tous";
∀x.dog(x)
signifie "pour tout x, x est un chien." "Λ", c'est le capital lambda, utilisées pour le captage sur les paramètres de type; ce que la deuxième ligne indique que l'id est une fonction qui prend un typet
, puis retourne une fonction qui est paramétrée par le type.Vous voyez, dans le Système F, vous ne pouvez pas appliquer une fonction comme ça
id
à une valeur tout de suite; vous devez d'abord appliquer le Λ-fonction d'un type afin d'obtenir un λ-fonction que vous appliquez à une valeur. Ainsi, par exemple:Standard Haskell (c'est à dire, Haskell 98 et 2010) simplifie pour vous en n'ayant aucun de ces types de quantificateurs, capitale des lambdas et les applications de type, mais en coulisses, le GHC met quand il analyse le programme pour la compilation. (C'est tout au moment de la compilation de trucs, je crois, avec pas de gestion d'exécution.)
Mais Haskell manipulation automatique, cela signifie qu'il suppose que "∀" n'apparaît jamais sur la gauche de la branche d'une fonction ("→") de type.
Rank2Types
etRankNTypes
éteindre les restrictions et vous permettent de remplacer Haskell des règles par défaut pour l'endroit où insérerforall
.Pourquoi voudriez-vous faire cela? Parce que le plein, sans restriction de F est hella puissant, et il peut faire beaucoup de trucs cool. Par exemple, le type de la clandestinité et de la modularité peut être mis en œuvre à l'aide de rang supérieur à partir de types. Prenez par exemple un simple vieux de la fonction du rang suivant-1 de type (de la scène):
À utiliser
f
, l'appelant doit d'abord choisir les types à utiliser pourr
eta
, puis fournir un argument de type résultant. Si vous pouviez choisirr = Int
eta = String
:Mais maintenant, comparez cela à la suite de plus haut rang type:
Comment fonctionne une fonction de ce type de travail? Ainsi, pour l'utiliser, il faut d'abord spécifier le type à utiliser pour
r
. Dites-nous choisirInt
:Mais maintenant, le
∀a
est à l'intérieur de la fonction de flèche, de sorte que vous ne pouvez pas choisir ce type à utiliser poura
; vous devez appliquerf' Int
pour un Λ-fonction du type approprié. Cela signifie que la mise en œuvre def'
obtient à choisir ce type à utiliser poura
, pas l'appelant def'
. Sans de plus haut rang types, au contraire, l'appelant choisit toujours les types.Quoi est-ce utile pour? Eh bien, pour beaucoup de choses en fait, mais l'idée est que vous pouvez l'utiliser pour modéliser des choses comme la programmation orientée objet, où les "objets" bundle cachées des données en collaboration avec certaines méthodes de travail sur les données cachées. Ainsi, par exemple, un objet avec les deux méthodes, l'une qui renvoie un
Int
et un autre qui renvoie unString
, pourraient être mises en œuvre avec ce type:Comment cela fonctionne? L'objet est mis en œuvre comme une fonction qui a des données internes de type hidden
a
. En fait d'utiliser l'objet, à ses clients de passer dans un "rappel" de la fonction que l'objet d'appels avec les deux méthodes. Par exemple:Nous voici, en gros, en invoquant l'objet de la deuxième méthode, celle dont le type est
a → String
pour un inconnua
. Eh bien, inconnu àmyObject
s clients; mais ces clients ne sais, à partir de la signature, qu'ils seront en mesure d'appliquer l'une des deux fonctions, une pour elle, et obtenir uneInt
ou unString
.Pour un Haskell exemple, ci-dessous le code que j'ai écrit quand j'ai appris moi-même
RankNTypes
. Il implémente un type appeléShowBox
qui rassemble une valeur d'un type hidden avec sesShow
instance de classe. Notez que dans l'exemple en bas, je fais une liste deShowBox
dont le premier élément est fabriqué à partir d'un nombre, et le second à partir d'une chaîne. Depuis les types sont cachés à l'aide de la plus-rang types, ce n'est pas violer la vérification de type.PS: pour tout le monde la lecture de ce qui est demandé comment venir
ExistentialTypes
dans GHC utiliseforall
, je crois que la raison est parce que c'est à l'aide de ce genre de technique derrière les coulisses.exists
mot-clé, vous pouvez définir une existentiel type que (par exemple)data Any = Any (exists a. a)
, oùAny :: (exists a. a) -> Any
. À l'aide ∀x.P(x) → Q ≡ (∃x.P(x)) → Q, nous pouvons conclure queAny
pourrait également avoir un typeforall a. a -> Any
et c'est là que leforall
mot-clé vient. Je crois que existentielle types de mises en œuvre par GHC ne sont que de simples types de données qui a également transporter tout le nécessaire typeclass dictionnaires (je ne pouvais pas trouver une référence à une copie de sauvegarde, désolé).data ApplyBox r = forall a. ApplyBox (a -> r) a
; lorsque vous en correspondance du modèle deApplyBox f x
, vous obtenezf :: h -> r
etx :: h
pour un "caché" restreinte typeh
. Si je comprends bien droit, le typeclass dictionnaire de cas est traduit en quelque chose comme ceci:data ShowBox = forall a. Show a => ShowBox a
est traduit en quelque chose commedata ShowBox' = forall a. ShowBox' (ShowDict' a) a
;instance Show ShowBox' where show (ShowBox' dict val) = show' dict val
;show' :: ShowDict a -> a -> String
.b
type ShowBox = forall b. (forall un. Montrer que a => a -> b) -> b est-elle vraiment nécessaire? Je pense qu'il serait plus propre si vous venez d'écrire:type ShowBox = (forall a. Show a => a -> String) -> String
runShowBox :: (forall a. Show a => a -> String) -> ShowBox -> String
ou de fournir d'autres exemple où polimorphicb
a l'utilisation pratique (showsPrec peut-être?) Sinon il juste introduit une complexité inutile, ne sont pas nécessaires pour la compréhension de rang supérieur à partir de types.Luis Casillas réponse donne beaucoup d'info sur ce rang 2 types veux dire, mais je vais simplement vous développez sur un point, il n'a pas de couverture. Nécessitant un argument pour être polymorphe n'est pas seulement lui permettre d'être utilisé avec plusieurs types; elle restreint également que cette fonction peut faire avec son argument(s) et comment il peut produire son résultat. C'est, il donne à l'appelant moins flexibilité. Pourquoi voudriez-vous faire cela? Je vais commencer par un exemple simple:
Supposons que nous avons un type de données
et nous voulons écrire une fonction
qui prend une fonction qui est censé choisir un des éléments de la liste, elle est donnée et le retour d'un
IO
action de lancer des missiles sur la cible. Nous pourrions donnerf
un type simple:Le problème est que nous pourrions exécutez accidentellement
et alors nous serions en grande difficulté! Donner
f
un rang 1 de type polymorphen'aide pas du tout, parce que nous choisissons le type
a
lorsque nous appelonsf
, et de nous spécialiser àCountry
et utilisez notre malveillants\_ -> BestAlly
de nouveau. La solution est d'utiliser un rang 2 type:Maintenant la fonction que nous avons pass est requis pour être polymorphe, donc
\_ -> BestAlly
ne sera pas de vérification de type! En fait, pas de fonction retour d'un élément dans la liste, il est remis typecheck (bien que certaines des fonctions qui vont dans une boucle infinie ou de produire des erreurs et, par conséquent, ne reviennent jamais le fera).Ci-dessus est tiré par les cheveux, bien sûr, mais une variante de cette technique est la clé pour faire de la
ST
monade coffre-fort.De plus haut rang types ne sont pas aussi exotique que les autres réponses ont fait. Le croire ou pas, de nombreux langages orientés objet (notamment Java et C#!) cette fonctionnalité. (Bien sûr, personne dans ces communautés, les connaît par l'effrayant nom à consonance "de haut rang types".)
L'exemple que je vais donner est un manuel de mise en œuvre du modèle Visiteur, que j'utilise tout le temps dans mon travail quotidien. Cette réponse n'est pas conçu comme une introduction au modèle visiteur; que la connaissance est facilement disponible d'ailleurs.
Dans cette fastueuse imaginaire application RH, nous souhaitons opérer sur les employés qui peuvent être à temps plein du personnel permanent ou temporaire des entrepreneurs. Mon préféré variante du modèle Visiteur (et en effet celui qui est pertinent pour la
RankNTypes
) parameterises du visiteur, le type de retour.Le point est que un certain nombre de visiteurs avec différents types de retour peuvent tous fonctionner sur les mêmes données. Cela signifie
IEmployee
doit exprimer aucune opinion quant à ce quiT
devrait être.Je tiens à attirer votre attention sur les types. Observer que
IEmployeeVisitor
universellement quantifie son type de retour, alors queIEmployee
quantifie l'intérieur de sonAccept
méthode - c'est-à-dire, à un rang plus élevé. La traduction de clunkily de C# à Haskell:Donc là vous l'avez. De plus haut rang types montrer en C# lorsque vous écrivez contenant des méthodes génériques.
Diapositives à partir de Bryan O'Sullivan de Haskell cours à l'université de Stanford m'a aidé à comprendre
Rank2Types
.Pour ceux qui sont familiers avec les langages orientés objets, un de plus haut grade de la fonction est simplement une fonction générique qui attend comme argument d'une autre fonction générique.
E. g. en caractères d'imprimerie, vous pourriez écrire:
Voir comment la fonction générique de type
Identify
exigences d'une fonction générique de typeIdentifier
? Cela rendIdentify
un de plus haut grade de la fonction.IEmployee
accepte le monomorphe type deIEmployeeVisitor<T>
. Le typeIEmployeeVisitor
a été pleinement appliquée. Il n'est de rang supérieur à taper si vous acceptez de vous ouvrir les types polymorphes.Accept
a un rang-1 de type polymorphe, mais c'est une méthode deIEmployee
, qui lui-même est de rang 2. Si quelqu'un me donne uneIEmployee
, je peux l'ouvrir et d'utiliser sesAccept
méthode à n'importe quel type.forall
se produit dans une position où il ne peut pas être flottait à la GAUCHE, c'est à dire dans un intégré à l'open de type polymorphe. Benjamin exemple tous les types polymorphes sont de rang 1: en fait, je peux écrire son exemple en Haskell, sans l'aide deRank2Types
ou toutes les extensions à tous pour cette question. Ici: gist.github.com/masaeedu/30adb29ad4b1d0cfd1ae65aaedf7ad9eVisitee
classe que vous introduire. Une fonctionf :: Visitee e => T e
est (une fois que la classe des choses est délactosé) essentiellementf :: (forall r. e -> Visitor e r -> r) -> T e
. Haskell 2010 vous permet de sortir avec peu de rang 2 à l'aide de classes polymorphisme comme ça.forall
dans le type n'a pas de rang 2. C'est seulement le rang 2 si vous vous ne pouvez pas float à gauche comme ça. Une fonction simple de la définition de type, commeid :: x -> x
a implicitement unforall
en elle, c'est à dire, à proprement parler, c'estid :: forall x. x -> x
, mais ce n'en est pas un classement de type 2.forall
dans mon exemple. Je n'ai pas de référence pour la main, mais vous pouvez très bien trouver quelque chose dans "Scrap Vos Classes de Type". De plus haut rang polymorphisme peut en effet introduire la vérification de type de problèmes, mais le peu de tri implicite dans le système de classe est très bien.