Was ist der Zweck der Rank2Types?
Ich bin nicht wirklich bewandert in Haskell, so könnte dies eine sehr einfache Frage.
Welche Sprache Einschränkung tun Rank2Types lösen? Nicht Funktionen, die in Haskell bereits die Unterstützung polymorphe Argumente?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Tun Sie, aber nur Rang 1. Dies bedeutet, dass, während Sie können eine Funktion schreiben, die nimmt verschiedene Arten von Argumenten ohne diese Erweiterung, Sie können nicht schreiben Sie eine Funktion mit Ihrem argument als verschiedene Arten in dem gleichen Aufruf.
Beispielsweise mit der folgenden Funktion kann nicht eingegeben werden, ohne diese Erweiterung, weil
g
verwendet wird, mit unterschiedlichen argument-Typen in die definition derf
:Beachten Sie, dass es durchaus möglich ist, übergeben Sie eine polymorphe Funktion als argument an eine andere Funktion. So etwas wie
map id ["a","b","c"]
ist vollkommen legal. Aber die Funktion können Sie nur verwenden Sie es als monomorphe. Im Beispielmap
verwendetid
als hätte es gebenString -> String
. Und natürlich kann man auch an eine einfache monomorphe Funktion des gegebenen Typs stattid
. Ohne rank2types es keine Möglichkeit gibt, für eine Funktion zu verlangen, dass sein argument muss eine polymorphe Funktion und somit auch keine Möglichkeit, es zu benutzen, wie eine polymorphe Funktion.f' g x y = g x + g y
. Seine abgeleitete Rang-1-Typ istforall a r. Num r => (a -> r) -> a -> a -> r
. Daforall a
ist außerhalb der Funktion Pfeile, muss der Anrufer zuerst wählen Sie einen Typ füra
; wenn Sie pickInt
bekommen wirf' :: forall r. Num r => (Int -> r) -> Int -> Int -> r
, und jetzt haben wir Feste dieg
argument, so kann esInt
aber nichtString
. Wenn wir ermöglichenRankNTypes
wir beschriften könnenf'
mit Typ -forall b c r. Num r => (forall a. a -> r) -> b -> c -> r
. Kann es nicht verwenden, obwohl—was wäreg
werden?Es ist schwer zu verstehen, höheren Rang Polymorphismus, es sei denn, Sie studieren System F direkt, denn Haskell ist entworfen, um zu verbergen die details der von Ihnen im Interesse der Einfachheit.
Aber im Grunde genommen, die grobe Idee ist, dass polymorphe Typen haben nicht wirklich die
a -> b
form, dass Sie in Haskell; in Wirklichkeit sehen Sie so wie hier, immer mit expliziten Quantoren:Wenn Sie nicht wissen, die " ∀ " - symbol, es ist zu Lesen als "für alle";
∀x.dog(x)
bedeutet "für alle x, x ist ein Hund." "Λ" ist die Hauptstadt lambda-Ausdruck, der verwendet, für die Abstraktion über Typ-Parameter; was die zweite Zeile sagt, dass id ist eine Funktion, eine Artt
, und gibt dann eine Funktion, parametrisiert durch, der Typ.Sehen Sie, System F, Sie können nicht nur eine Funktion wie, dass
id
auf einen Wert gleich; zuerst müssen Sie auf anwenden, um die Λ-Funktion auf einen Typ, um eine λ-Funktion, die Sie auf einen Wert. So zum Beispiel:Standard-Haskell (D. H., Haskell 98 und 2010) vereinfacht dies für Sie nicht mit jeder dieser Art Quantoren -, Kapital-Lambda-Ausdrücke und Typ-Anwendungen, aber hinter den kulissen GHC setzt Sie ein, wenn es analysiert das Programm für die Zusammenstellung. (Dies ist alle compile-Zeit-Zeug, glaube ich, keine Laufzeit-overhead.)
Aber Haskell ist die automatische Handhabung von das heißt, es wird davon ausgegangen, dass "∀" erscheint nie auf der linken hand Zweig einer Funktion ("→") geben.
Rank2Types
undRankNTypes
schalten Sie diese Einschränkungen und erlauben Sie das überschreiben Haskell die default-Regeln für die, wo zum einfügenforall
.Warum würden Sie wollen, dies zu tun? Weil die volle, uneingeschränkte System F ist hella mächtig, und es kann eine Menge Coole Sachen. Geben Sie beispielsweise ausblenden und Modularität umgesetzt werden können, mit höheren Rang-Typen. Nehmen Sie zum Beispiel eine einfache alte Funktion der folgenden Rang-1-Typ (in Szene gesetzt):
Verwenden
f
, der Anrufer muss zunächst auswählen, welche Art zu verwenden, fürr
unda
, liefern dann ein argument des resultierenden Typs. So konnte man wählenr = Int
unda = String
:Aber jetzt vergleichen Sie das mit den folgenden höheren Rang Typ:
Wie sieht eine Funktion dieser Art Arbeit? Naja, zu verwenden, müssen Sie zuerst bestimmen Sie, welche Art zu verwenden, für
r
. Sagen wir HolenInt
:Aber jetzt die
∀a
ist innen die Funktion Pfeil, so können Sie nicht auswählen, welche Art zu verwenden, füra
; Sie müssenf' Int
zu einem Λ-Funktion des entsprechenden Typs. Dies bedeutet, dass die Umsetzung derf'
bekommt zu wählen, welche Art zu verwenden, füra
nicht den Aufrufer derf'
. Ohne höheren Rang Typen, im Gegenteil, der Anrufer wählt immer die Typen.Was ist dieser nützlich? Gut, für viele Dinge, eigentlich, aber eine Idee ist es, dass Sie können mit diesem Modell Dinge wie Objekt-orientierte Programmierung, wo die "Objekte" - bundle einige versteckte Daten zusammen mit einigen Methoden, die auf die versteckten Daten. So zum Beispiel ein Objekt mit zwei Methoden—eine, gibt eine
Int
und ein anderes, das gibt eineString
, umgesetzt werden kann, die mit dieser Art:Wie funktioniert das? Das Objekt ist implementiert als eine Funktion, die einige interne Daten von versteckten Typ
a
. Um tatsächlich mit dem Objekt, seinen Kunden eine "callback" - Funktion, die das Objekt aufrufen, mit den zwei Methoden. Zum Beispiel:Hier sind wir, im Grunde, in dem das Objekt die zweite Methode, deren Typ ist
a → String
für eine unbekanntea
. Gut, unbekanntmyObject
's Kunden, aber diese Kunden wissen, von der Unterschrift, dass Sie werde in der Lage sein, gelten entweder die beiden Funktionen, und erhalten Sie entweder eineInt
oder eineString
.Für eine tatsächliche Haskell Beispiel unten ist der code, den ich schrieb, als ich mir selbst beigebracht
RankNTypes
. Diese implementiert einen Typ namensShowBox
die bundles, die zusammen einen Wert von einigen versteckten Typ zusammen mit seinenShow
Instanz der Klasse. Beachten Sie, dass im Beispiel unten habe ich eine Liste machenShowBox
dessen erstes element wurde aus einer Zahl und die zweite aus einem string. Da die Typen werden ausgeblendet, indem Sie mit der höheren rank-Typen, diese nicht gegen die überprüfung von Typ.PS: für jemand der Lektüre dieses wer gefragt, wie kommen
ExistentialTypes
im GHC verwendetforall
ich glaube, der Grund ist, weil es mit dieser Art von Technik hinter den kulissen.exists
Schlüsselwort, könnten Sie eine existenzielle Art wie (zum Beispiel)data Any = Any (exists a. a)
, woAny :: (exists a. a) -> Any
. Mit ∀x.P(x) → Q ≡ (∃x.P(x)) → Q, können wir schließen, dassAny
könnte auch eine Artforall a. a -> Any
- und das ist, wo dieforall
keyword kommt. Ich glaube, dass existenzielle Arten implementiert werden, von GHC sind nur normale Daten-Typen, die tragen auch alle erforderlichen typeclass Wörterbücher (ich konnte nicht finden, einen Verweis auf das sichern, sorry).data ApplyBox r = forall a. ApplyBox (a -> r) a
; wenn Sie pattern-match zuApplyBox f x
erhalten Sief :: h -> r
undx :: h
für ein "versteckter," nur für den Dienstgebrauch Typh
. Wenn ich verstehe richtig, der typeclass Wörterbuch Fall ist übersetzt in etwa wie folgt:data ShowBox = forall a. Show a => ShowBox a
heißt übersetzt so etwas wiedata ShowBox' = forall a. ShowBox' (ShowDict' a) a
;instance Show ShowBox' where show (ShowBox' dict val) = show' dict val
;show' :: ShowDict a -> a -> String
.b
im Typ ShowBox = forall b. (forall ein. Zeigen Sie a => a -> b) -> b ist, auch wirklich notwendig? Ich denke, es wäre mehr sauber, wenn Sie nur schreiben:type ShowBox = (forall a. Show a => a -> String) -> String
runShowBox :: (forall a. Show a => a -> String) -> ShowBox -> String
oder zusätzliche Beispiel, wo polimorphicb
hat praktischen nutzen (showsPrec vielleicht?) Ansonsten, es führt nur zu unnötiger Komplexität, nicht benötigt für das Verständnis höherer Rang-Typen.Luis Casillas Antwort gibt eine Menge tolle Informationen über das, was Rang 2 Typen bedeuten, aber ich werde einfach erweitern auf eine Stelle, die er nicht abdecken. Erfordert ein argument, daß polymorphe nicht nur ermöglichen den Einsatz mit mehreren Arten; es schränkt auch was die Funktion tun können, mit seinem argument(s) und wie kann es Ihr Ergebnis produzieren. Das heißt, es gibt die Anrufer weniger Flexibilität. Warum wollen Sie das tun? Ich werde beginnen mit einem einfachen Beispiel:
Nehmen wir an, wir haben einen Datentyp
und wir möchten eine Funktion schreiben, die
nimmt, dass eine Funktion, die angeblich wählen Sie eines der Elemente von der Liste, es ist gegeben und eine
IO
Aktion starten Raketen auf das Ziel. Wir gebenf
einfache Art:Das problem ist, dass wir versehentlich ausführen
und dann wären wir in großen Schwierigkeiten! Geben
f
Rang 1 polymorphe Typüberhaupt nicht helfen, weil wir wählen, die Art
a
wenn wir rufenf
, und wir spezialisieren es zuCountry
und nutzen Sie unsere bösartige\_ -> BestAlly
wieder. Die Lösung ist die Verwendung eines Rang-2 Typ:Nun die Funktion, die wir übergeben, in der gefordert wird, polymorphe, also
\_ -> BestAlly
nicht Typ-check! In der Tat, keine Funktion der Rückkehr ein element nicht in der Liste ist es gegeben, wird typecheck (obwohl einige Funktionen, die in Endlosschleifen gehen oder Fehler produzieren, und deshalb nie wieder tun wird).Oben ist künstlich, natürlich, aber eine variation dieser Technik ist der Schlüssel zu den
ST
Monade sicher.Höheren Rang Typen sind nicht so exotisch wie die anderen Antworten gemacht. Ob Sie es glauben oder nicht, viele objektorientierte Sprachen (wie Java und C#!) verfügen Sie. (Natürlich, niemand in diesen Gemeinden kennt Sie von den unheimlich klingenden Namen "higher-rank-Typen".)
Beispiel werde ich Ihnen ein lehrbuch Umsetzung des Visitor-pattern, welches ich die ganze Zeit in meiner täglichen Arbeit. Diese Antwort ist nicht gedacht als eine Einführung in das Besucher-Muster; dieses wissen ist leicht verfügbar anderswo.
In diesem albern imaginären HR-Anwendung, die wir wollen, zu arbeiten auf Mitarbeiter, die vielleicht Vollzeit-dauerstellen oder temporäre Auftragnehmer. Meine bevorzugte Variante des Visitor-pattern (und zwar eine, die relevant ist, um
RankNTypes
) parameterises die Besucher zurück geben.Der Punkt ist, dass eine Reihe von Besuchern mit unterschiedlichen Rückgabetypen können alle auf denselben Daten arbeiten. Dies bedeutet
IEmployee
muss Sie äußern keine Meinung, wasT
sein sollte.Möchte ich Ihre Aufmerksamkeit auf den Typen. Beachten Sie, dass
IEmployeeVisitor
universell quantifiziert seiner Rückkehr geben, in der Erwägung, dassIEmployee
quantifiziert, die es innerhalb seinerAccept
- Methode - das heißt, zu einem höheren Rang. Übersetzung clunkily von C# Haskell:So dort haben Sie es. Höherer Rang-Typen zeigen, bis in C#, wenn Sie schreiben Typen mit generischen Methoden.
Folien von Bryan O 'Sullivan' s Haskell-Kurs in Stanford mir geholfen zu verstehen
Rank2Types
.Für diejenigen, die vertraut mit objektorientierten Sprachen, mit einer höheren Rang-Funktion ist einfach eine generische Funktion, erwartet als argument eine weitere generische Funktion.
E. g. in Maschinenschrift, könnten Sie schreiben:
Sehen, wie die generische Funktion Typ
Identify
Anforderungen einer generischen Funktion der ArtIdentifier
? Dies machtIdentify
einen höheren Rang-Funktion.IEmployee
akzeptiert der monomorphe ArtIEmployeeVisitor<T>
. Der TypIEmployeeVisitor
wurde in vollem Umfang angewendet werden. Es ist nur höheren Rang eingeben, wenn Sie akzeptieren, öffnen, polymorphe Typen.Accept
hat ein Rang-1 polymorphe Art, aber es ist eine Methode derIEmployee
, was sich Rang-2. Wenn jemand gibt mir eineIEmployee
ich kann es öffnen, und verwenden Sie seineAccept
Methode, bei der jede Art.forall
tritt in einer position, wo es kann nicht sein, schwebte an die LS, d.h. in einem eingebetteten offenen polymorphen Typ. In Benjamins Beispiel alle polymorphen Typen sind Rang 1: in der Tat, ich kann schreiben, seinem Beispiel in Haskell ohne VerwendungRank2Types
oder Erweiterungen an allen für diese Angelegenheit. Hier: gist.github.com/masaeedu/30adb29ad4b1d0cfd1ae65aaedf7ad9eVisitee
Klasse, die Sie einführen. Eine Funktionf :: Visitee e => T e
ist (sobald die Klasse Zeug ist desugared) im wesentlichenf :: (forall r. e -> Visitor e r -> r) -> T e
. Haskell 2010 können Sie Weg mit begrenzten Rang-2-Polymorphismus mit Klassen wie, die.forall
in der Art nicht machen es zu Rang 2. Es ist nur Rang 2, wenn Sie nicht float es sich auf der linken Seite wie, die. Eine einfache Funktion-Typ-definition wieid :: x -> x
implizit eineforall
im es, d.h. genaugenommen ist esid :: forall x. x -> x
, aber das heißt nicht, dass es eine Rang 2-Typ.forall
in meinem Beispiel. Ich habe nicht eine Referenz aus der hand, aber Sie können etwas finden, in "Schrott-Ihre Typ-Klassen". Höherer Rang Polymorphismus kann in der Tat vorstellen, type-checking Probleme, aber die begrenzte Art implizit in der Klasse system ist in Ordnung.