Ich habe eine Sprache, in der Typen standardmäßig deaktiviert sind, wobei die Typinferenz auf Hindley-Milner basiert. Ich möchte einen höherrangigen Polymorphismus hinzufügen, hauptsächlich für die Arbeit mit existenziellen Typen.
Ich glaube, ich verstehe, wie man diese Typen überprüft , bin mir aber nicht sicher, was ich beim Kompilieren tun soll. Derzeit kompiliere ich polymorphe Definitionen, indem ich Spezialisierungen generiere, ähnlich wie C ++ - Vorlagen, damit sie mit Werten ohne Box arbeiten können. f<T>
Wenn bei einer Definition von beispielsweise , wenn das Programm nur f<Int32>
und aufruft f<Char>
, nur diese Spezialisierungen im kompilierten Programm angezeigt werden. (Ich gehe vorerst von einer Zusammenstellung des gesamten Programms aus.)
Wenn ich jedoch eine polymorphe Funktion als Argument übergebe, sehe ich nicht, wie ich die richtige Spezialisierung statisch generieren kann, da die Funktion zur Laufzeit ausgewählt werden könnte. Habe ich keine andere Wahl, als eine Boxdarstellung zu verwenden? Oder gibt es einen Weg, um das Problem zu umgehen?
Mein erster Gedanke war, Rang- n- Polymorphismus irgendwie als Rang 1 zu kodieren , aber ich glaube nicht, dass dies im Allgemeinen möglich ist, da eine Formel in der konstruktiven Logik nicht unbedingt eine Prenex-Normalform hat.
quelle
Antworten:
Ich habe ein bisschen darüber nachgedacht. Das Hauptproblem ist, dass wir im Allgemeinen nicht wissen, wie groß der Wert des polymorphen Typs ist. Wenn Sie diese Informationen nicht haben, müssen Sie sie irgendwie bekommen. Die Monomorphisierung erhält diese Informationen für Sie, indem sie sich auf den Polymorphismus spezialisiert. Boxing erhält diese Informationen für Sie, indem es alles in eine Darstellung bekannter Größe bringt.
Eine dritte Alternative besteht darin, diese Informationen in den Arten zu verfolgen. Grundsätzlich können Sie für jede Datengröße eine andere Art einführen, und dann können polymorphe Funktionen für alle Typen einer bestimmten Größe definiert werden. Ich werde ein solches System unten skizzieren.
Hier besteht die übergeordnete Idee darin, dass die Art eines Typs angibt, wie viele Wörter erforderlich sind, um ein Objekt im Speicher auszulegen. Für jede gegebene Größe ist es einfach, über alle Arten dieser bestimmten Größe polymorph zu sein. Da jeder Typ - auch polymorphe - immer noch eine bekannte Größe hat, ist die Kompilierung nicht schwieriger als bei C.
Referenzen sind interessant - Zeiger sind immer ein Wort, können aber auf Werte beliebiger Größe verweisen. Auf diese Weise können Programmierer implementieren Polymorphismus auf beliebige Objekte durch Boxen, aber nicht erforderlich , sie so zu tun. Sobald explizite Größen im Spiel sind, ist es oft nützlich, einen Polstertyp einzuführen, der Platz beansprucht, aber nichts tut. (Wenn Sie also die disjunkte Vereinigung eines int und eines Int-Paares verwenden möchten, müssen Sie das erste int auffüllen, damit das Objektlayout einheitlich ist.)
Rekursive Typen haben die Standardformationsregel. Beachten Sie jedoch, dass rekursive Vorkommen dieselbe Größe haben müssen. Dies bedeutet, dass Sie sie normalerweise in einen Zeiger stecken müssen, damit die Sortierung funktioniert. Beispielsweise könnte der Listendatentyp als dargestellt werden
Dies zeigt also auf einen leeren Listenwert oder ein Paar aus einem int und einem Zeiger auf eine andere verknüpfte Liste.
Die Typprüfung für solche Systeme ist ebenfalls nicht sehr schwierig. Der Algorithmus in meinem ICFP- Artikel mit Joshua Dunfield, Vollständige und einfache bidirektionale Typprüfung für höherrangigen Polymorphismus, gilt für diesen Fall nahezu unverändert .
quelle
*
argumentieren (wie GHC's vs.#
), hatte aber nicht darüber nachgedacht, dies so zu tun. Es erscheint vernünftig, höherrangige Quantifizierer auf Typen bekannter Größe zu beschränken, und ich denke, dies würde es mir auch ermöglichen, statisch Spezialisierungen pro Größe zu generieren, ohne den tatsächlichen Typ kennen zu müssen. Jetzt ist es Zeit, das Papier noch einmal zu lesen. :)Dies scheint eher einem Kompilierungsproblem als einem "theoretischen Informatik" -Problem zu entsprechen, daher ist es wahrscheinlich besser, wenn Sie anderswo nachfragen.
Im allgemeinen Fall gibt es meiner Meinung nach keine andere Lösung als die Verwendung einer Boxed-Darstellung. Ich erwarte aber auch, dass es in der Praxis viele verschiedene alternative Optionen gibt, abhängig von den Besonderheiten Ihrer Situation.
Beispielsweise kann die Darstellung von Argumenten ohne Box auf niedriger Ebene normalerweise in sehr wenige Alternativen eingeteilt werden, z. B. Ganzzahl oder ähnlich, Gleitkomma oder Zeiger. Für eine Funktion
f<T>
müssen Sie möglicherweise nur drei verschiedene Implementierungen ohne Box generieren und können die polymorphe als Tupel dieser drei Funktionen darstellen. Wenn Sie also T zu Int32 instanziieren, wählen Sie einfach das erste Element des Tupels aus, ...quelle