Typinferenz implementieren

90

Ich sehe hier einige interessante Diskussionen über statische und dynamische Typisierung. Ich bevorzuge im Allgemeinen die statische Typisierung aufgrund der Überprüfung des Kompiliertyps, besser dokumentierten Codes usw. Ich stimme jedoch zu, dass sie den Code überladen, wenn sie beispielsweise so ausgeführt werden, wie Java es tut.

Ich bin also dabei, eine eigene funktionale Stilsprache zu erstellen, und Typinferenz ist eines der Dinge, die ich implementieren möchte. Ich verstehe, dass es ein großes Thema ist, und ich versuche nicht, etwas zu erschaffen, was vorher noch nicht getan wurde, sondern nur grundlegende Schlussfolgerungen ...

Irgendwelche Hinweise, was ich lesen soll, die mir dabei helfen? Am liebsten etwas pragmatischeres / praktischeres als theoretischere kategorietheoretische / typentheoretische Texte. Wenn es da draußen einen Implementierungstext mit Datenstrukturen / Algorithmen gibt, wäre das einfach schön.

tiefes Blau
quelle
1
Genau die Frage, nach der ich gesucht habe, mit einigen großartigen Antworten!
Paul Hollingsworth

Antworten:

89

Ich fand die folgenden Ressourcen hilfreich, um die Typinferenz in der Reihenfolge zunehmender Schwierigkeiten zu verstehen:

  1. Kapitel 30 ( Typinferenz ) des frei verfügbaren Buches PLAI , Programmiersprachen: Anwendung und Interpretation , skizziert vereinheitlichungsbasierte Typinferenz.
  2. Der Sommerkurs Interpretieren von Typen als abstrakte Werte präsentiert elegante Bewerter, Typprüfer, Typrekonstruktoren und Inferencer, die Haskell als Metasprache verwenden.
  3. Kapitel 7 (Typen) des Buches EOPL , Grundlagen der Programmiersprachen .
  4. Kapitel 22 ( Typrekonstruktion ) des Buches TAPL , Typen und Programmiersprachen sowie die entsprechenden OCaml-Implementierungen Recon und Fullrecon .
  5. Kapitel 13 ( Typrekonstruktion ) des neuen Buches DCPL , Entwurfskonzepte in Programmiersprachen .
  6. Auswahl von wissenschaftlichen Arbeiten .
  7. Die TypeInference des Closure-Compilers ist ein Beispiel für den Ansatz der Datenflussanalyse zur Typinferenz, der sich besser für dynamische Sprachen eignet als der Hindler Milner-Ansatz.

Da der beste Weg zu lernen darin besteht, dies zu tun, empfehle ich dringend, eine Typinferenz für eine Spielzeugfunktionssprache zu implementieren, indem Sie eine Hausaufgabe eines Programmiersprachenkurses durcharbeiten.

Ich empfehle diese beiden zugänglichen Hausaufgaben in ML, die Sie beide in weniger als einem Tag erledigen können:

  1. PCF Interpreter ( eine Lösung ) zum Aufwärmen.
  2. PCF -Typinferenz ( eine Lösung ) zur Implementierung des Algorithmus W für die Hindley-Milner-Typinferenz.

Diese Aufgaben stammen aus einem fortgeschritteneren Kurs:

  1. MiniML implementieren

  2. Polymorphe, existentielle, rekursive Typen (PDF)

  3. Bidirektionales Typechecking (PDF)

  4. Untertypisierung und Objekte (PDF)

namin
quelle
2
Bin ich es nur, oder ist die PLAI-Beschreibung weitgehend falsch / unvollständig? Die Vorlesung fügt etwas mehr hinzu, scheint aber immer noch nicht genug zu sein, um sie zum Laufen zu bringen.
Rei Miyasaka
Ich konnte den Algorithmus auch in der 2012-Version des PLAI-Buches nicht bekommen. Es gibt keine Ersetzungen für die Einschränkungsliste. Stattdessen habe ich den Typinferenzalgorithmus in der Version 2003-7 des PLAI-Buches implementiert. Er scheint besser zu funktionieren und lässt sich auch gut auf Let-Polymorphismus skalieren.
Heykell
27

Es ist bedauerlich, dass ein Großteil der Literatur zu diesem Thema sehr dicht ist. Ich war auch in deinen Schuhen. Ich habe meine erste Einführung in das Thema von Programmiersprachen: Anwendungen und Interpretation bekommen

http://www.plai.org/

Ich werde versuchen, die abstrakte Idee zusammenzufassen, gefolgt von Details, die ich nicht sofort offensichtlich fand. Zunächst kann an eine Typinferenz gedacht werden, um Einschränkungen zu generieren und dann zu lösen. Um Einschränkungen zu generieren, durchlaufen Sie den Syntaxbaum und generieren eine oder mehrere Einschränkungen für jeden Knoten. Wenn der Knoten beispielsweise ein +Operator ist, müssen die Operanden und die Ergebnisse alle Zahlen sein. Ein Knoten, der eine Funktion anwendet, hat denselben Typ wie das Ergebnis der Funktion usw.

Für eine Sprache ohne letkönnen Sie die oben genannten Einschränkungen blind durch Ersetzen lösen. Beispielsweise:

(if (= 1 2) 
    1 
    2)

Hier können wir sagen, dass die Bedingung der if-Anweisung boolesch sein muss und dass der Typ der if-Anweisung mit dem Typ ihrer thenund else-Klauseln identisch ist . Da wir Zahlen kennen 1und 2sind, wissen wir durch Substitution, dass die ifAussage eine Zahl ist.

Wo die Dinge böse werden und was ich eine Weile nicht verstehen konnte, ist das Thema:

(let ((id (lambda (x) x)))
    (id id))

Hier haben wir uns an ideine Funktion gebunden , die alles zurückgibt, was Sie übergeben haben, auch bekannt als Identitätsfunktion. Das Problem ist, dass der Typ des Funktionsparameters xbei jeder Verwendung von unterschiedlich ist id. Die zweite idist eine Funktion vom Typ a -> a, wo aalles sein kann. Der erste ist vom Typ (a -> a) -> (a -> a). Dies ist als Let-Polymorphismus bekannt. Der Schlüssel besteht darin, Einschränkungen in einer bestimmten Reihenfolge zu lösen: Lösen Sie zuerst Einschränkungen für die Definition von id. Das wird sein a -> a. Dann können frische, separate Kopien des Typs von idin die Einschränkungen für jeden Ort ideingesetzt werden, zum Beispiel a2 -> a2und a3 -> a3.

Das wurde in Online-Ressourcen nicht ohne weiteres erklärt. Sie werden den Algorithmus W oder M erwähnen, aber nicht, wie sie beim Lösen von Einschränkungen funktionieren oder warum der Let-Polymorphismus nicht beeinträchtigt wird: Jeder dieser Algorithmen erzwingt eine Reihenfolge beim Lösen der Einschränkungen.

Ich fand diese Ressource äußerst hilfreich, um Algorithmus W, M und das allgemeine Konzept der Erzeugung und Lösung von Einschränkungen miteinander zu verknüpfen. Es ist ein wenig dicht, aber besser als viele:

http://www.cs.uu.nl/research/techreps/repo/CS-2002/2002-031.pdf

Viele der anderen Papiere dort sind auch nett:

http://people.cs.uu.nl/bastiaan/papers.html

Ich hoffe, das hilft, eine etwas trübe Welt zu klären.

Paul
quelle
6

Neben Hindley Milner für funktionale Sprachen ist ein weiterer beliebter Ansatz zur Typinferenz für dynamische Sprachen abstract interpretation.

Die Idee der abstrakten Interpretation besteht darin, einen speziellen Interpreter für die Sprache zu schreiben, anstatt eine Umgebung mit konkreten Werten (1, falsch, Abschluss) beizubehalten. Sie arbeitet mit abstrakten Werten, auch bekannt als Typen (int, bool usw.). Da das Programm auf abstrakten Werten interpretiert wird, wird es als abstrakte Interpretation bezeichnet.

Pysonar2 ist eine elegante Implementierung der abstrakten Interpretation für Python. Es wird von Google zur Analyse von Python-Projekten verwendet. Grundsätzlich wird visitor patternder Bewertungsaufruf an den relevanten AST-Knoten gesendet. Die Besucherfunktion transform akzeptiert den contextaktuellen Knoten, der ausgewertet wird, und gibt den Typ des aktuellen Knotens zurück.

Wei Chen
quelle
4

Typen und Programmiersprachen von Benjamin C. Pierce

Scott Wisniewski
quelle
3

Lambda the Ultimate beginnt hier .

Doug Currie
quelle