Musterübereinstimmung bei Funktionen auf der Typ-Ebene ist möglich, aber nicht auf der Werte-Ebene, warum ist dieser Unterschied?

8

In this < Ein Artikel von SPJ, auf Seite 3 und 4, steht geschrieben:

%Vor%

und:

  

Die Klassendeklaration führt jetzt eine Typefunktion Ref (mit a   angegebene Art) neben den üblichen Wertfunktionen wie newRef   (jeweils mit einem bestimmten Typ). Ebenso jede Instanz Deklaration   trägt eine Klausel bei, die die Typ-Funktion am Instanztyp definiert   neben einem Zeugen für jede Wertfunktion.

     

Wir sagen, dass Ref ein Typ ist   Familie oder ein zugehöriger Typ der Klasse Mutation. Es verhält sich wie ein    Funktion auf der Typ-Ebene , also rufen wir auch Ref a type function auf.   Das Anwenden einer Typfunktion verwendet die gleiche Syntax wie das Anwenden eines Typs   Konstruktor: Ref m a oben bedeutet, den Typ function Ref auf m anzuwenden,   Wenden Sie dann den resultierenden Typkonstruktor auf a an.

Also, mit anderen Worten,

Ref :: (*->*)->*->*

Das heißt, Ref verwendet eine Funktion auf Typfläche als Argument, wie Maybe oder IO oder [] und erzeugt eine andere Funktion auf Typebene, z. B. IORef mit einer Musterübereinstimmung , dh Ref wird durch eine Musterübereinstimmung definiert.

Wie also ist es möglich, dass Muster auf Funktionen auf Typ-Ebene, nicht aber auf Wert-Ebene angepasst werden können?

Zum Beispiel

%Vor%

ist nicht möglich zu schreiben, weil die Gleichheit der Funktionen unentscheidbar .

1) Wie ist es möglich, dass dies auf der Typenebene kein Problem verursacht?

2) Liegt es daran, dass die Funktionen auf der Typ-Ebene sehr eingeschränkt sind? Also kann keine Art von Funktion auf der Typ-Ebene ein Argument für Ref sein, nur ein paar ausgewählte, nämlich die vom Programmierer deklarierten und nicht wie (+), die allgemeiner als die vom Programmierer deklarierten sind ? Ist dies der Grund, warum auf der Typenebene die Übereinstimmung der Funktionsmuster kein Problem verursacht?

3) Bezieht sich die Antwort auf diese Frage auf dies Teil der GHC-Spezifikation?

    
jhegedus 31.12.2015, 08:48
quelle

2 Antworten

7

Kurz gesagt, es gibt keinen Musterabgleich für die Funktion Werte , sondern nur für ihren Namen .

In Haskell werden Typen wie in vielen anderen Sprachen durch ihren Namen getrennt, auch wenn ihre Darstellung identisch ist.

%Vor%

Oben, A und B sind zwei verschiedene Typkonstruktoren, selbst wenn sie dieselbe Typ-Level-Funktion beschreiben: in Pseudo-Code \x -> (Int, x) , ungefähr. In gewisser Hinsicht haben diese beiden identischen Funktionen auf Typenebene einen anderen Namen / eine andere Identität.

Dies ist anders als

%Vor%

, die beide die gleiche Typ-Level-Funktion wie oben beschreiben, aber nicht zwei neue Typnamen einführen. Die obigen sind nur Synonyme: Sie bezeichnen eine Funktion, haben aber keine eigene Identität.

Aus diesem Grund kann man eine Klasseninstanz für A x oder B x hinzufügen, aber nicht für C x oder D x : Beim Versuch, Letzteres zu tun, würde stattdessen eine Instanz zum Typ (Int, x) hinzugefügt die Instanz zu den Typnamen (,) , Int stattdessen.

Auf der Werteebene ist die Situation nicht so sehr anders. Tatsächlich gibt es Wertkonstruktoren, die spezielle Funktionen mit einem Namen / einer Identität und reguläre Funktionen ohne eine tatsächliche Identität sind. Wir können Muster mit einem Muster abgleichen, das aus Konstruktoren, aber nicht aus anderen Elementen erstellt wurde

%Vor%

Beachten Sie, dass wir auf Musterebene keine einfache Mustererkennung durchführen können. Haskell erlaubt das nur mit Typklassen. Dies geschieht, um einige theoretische Eigenschaften (Parametrik) beizubehalten und auch eine effiziente Implementierung zu ermöglichen (erlaubt das Löschen von Typen - wir müssen nicht jeden Wert mit seinem Typ zur Laufzeit kennzeichnen). Diese Funktionen haben den Preis, dass die Typ-Level-Mustererkennung auf Typklassen beschränkt wird - dies belastet zwar den Programmierer, aber die Vorteile überwiegen die Nachteile.

    
chi 31.12.2015, 09:55
quelle
7
%Vor%

Lassen Sie uns einige Parallelen zwischen dem Typ und der Wertstufe in Haskell ziehen.

Zuerst haben wir uneingeschränkte Funktionen sowohl auf Typ- als auch auf Wertebene. Auf der Typenebene können Sie nahezu alles mithilfe von Typfamilien ausdrücken. Sie können Muster nicht mit beliebigen Funktionen auf Typ- oder Werteebene abgleichen. Z.B. Du kannst nicht sagen

%Vor%

Zweitens haben wir Daten und Typkonstruktoren vollständig angewendet, z. B. Just 5 :: Maybe Integer auf der Werteebene oder Just 5 :: Maybe Nat auf der Typenebene.

Es ist möglich, auf diese Muster eine Übereinstimmung zu finden:

%Vor%

Beachten Sie den Unterschied zwischen beliebigen Funktionen und Typ / Daten -Konstruktoren . Die Eigenschaft von Konstruktoren wird manchmal Generativität genannt. Für zwei verschiedene Funktionen f und g ist es sehr gut möglich, dass f x = g x für einige x . Auf der anderen Seite bedeutet f x = g x für Konstruktoren f = g . Dieser Unterschied macht den ersten Fall (Mustervergleich bei beliebigen Funktionen) unentscheidbar und den zweiten Fall (Mustervergleich bei vollständig angewendeten Konstruktoren) entscheidbar und handhabbar.

Bisher haben wir eine Konsistenz über die Art und die Werte hinweg gesehen.

Endlich haben wir teilweise Konstruktoren angewendet (einschließlich nicht angewendet). Auf der Typenebene gehören Maybe , IO und [] (nicht angewendet) sowie Either String und (,) Int (teilweise angewendet). Auf der Werteebene haben wir Just und Left nicht angewendet und (,) 5 und (:) True teilweise angewendet.

Die Generativitätsbedingung interessiert nicht, ob die Anwendung voll ist oder nicht; Es gibt also nichts, was den Mustervergleich für teilweise angewandte Konstruktoren ausschließt. Das sehen Sie auf der Typenebene; und wir könnten es auch auf der Wertebene haben.

%Vor%

Der Grund, warum dies nicht unterstützt wird, ist die Effizienz. Berechnungen auf der Typ-Ebene werden während der Kompilierzeit im interpretierten Modus ausgeführt; also können wir es uns leisten Umgehen Sie viele Metadaten über Typen und Typ Funktionen, um Muster übereinstimmen auf sie.

Berechnungen auf der Werteebene werden kompiliert und müssen so schnell sein wie möglich. Halten Sie genügend Metadaten, um die Mustererkennung teilweise zu unterstützen Angewandte Konstruktoren würden den Compiler verkomplizieren und das Programm verlangsamen Laufzeit; es ist einfach zu viel, um so ein exotisches Feature zu bezahlen.

    
Roman Cheplyaka 31.12.2015 10:15
quelle