Wäre es möglich, der C-Sprache Typinferenz hinzuzufügen?
Lesezeit: 8 Minuten
Deontologe
Nehmen wir an, wir erstellen eine Neuimplementierung von C, mit dem einzigen Unterschied, dass Typen abgeleitet werden. Speicherklassen und Modifikatoren müssten noch angegeben werden (const, static, strict usw.), und beschränken wir unsere Aufmerksamkeit zunächst auf C-Programme mit einzelnen Dateien. Könnte es gemacht werden? Was sind die größten Hindernisse?
Einige Gedanken darüber, was Probleme bei der Typinferenz verursachen könnte
Strukturen mit demselben Feldnamen müssten manuell disambiguiert werden
Gleiches gilt für Gewerkschaften mit denselben Feldnamen
Casts würden wahrscheinlich eine „von“-Anmerkung benötigen, so etwas wie
var i = (uint32_t -> uint64_t) *some_pointer;
Diese Probleme würden ein wenig Benutzeranmerkung erfordern, sollten aber nicht zu belastend sein. Gibt es ein Killerproblem, das diese Idee zunichte macht?
Bearbeiten: Zur Klarstellung: Ich spreche nicht über das Hinzufügen von Generika oder parametrischem Polymorphismus, sondern nur über die Typinferenz für vorhandene C-Typen.
Bearbeiten 2014: Jeder, der sich für dieses Konzept interessiert, sollte es sich ansehen Rost
Ich bin wahrscheinlich langsam; Können Sie ein Beispiel für Ihren ersten Aufzählungspunkt nennen?
– Oliver Charlesworth
28. Juni 2011 um 22:31
Was Sie beschreiben, klingt eher nach einem dynamischen Typsystem. Typinferenz ist insgesamt eine andere Idee.
– Jeff Mercado
28. Juni 2011 um 22:32
Ich glaube nicht, dass ich das Fachwissen habe, um eine vollständige Antwort einzureichen, aber meine Intuition sagt, dass dies durchaus machbar ist, zumindest für eine C-ähnliche Sprache, die vielleicht 90–95 % von C abdeckt. Wir haben bereits eine brauchbare Typinferenz dafür Python (siehe das ShedSkin-Projekt), daher könnte ich mir vorstellen, dass ähnliche Techniken für eine (oder nahezu) Version von C ohne Typdeklaration verwendet werden könnten.
– John Y
28. Juni 2011 um 22:34
@pmg: Typinferenz hat nichts mit bizarren automatischen Typkonvertierungen zu tun. Tatsächlich interagiert es ziemlich schlecht mit ihnen.
– Chuck
28. Juni 2011 um 22:39
@pmg: In C, wie es jetzt ist, handelt es sich um ein undefiniertes Verhalten für Ihren speziellen Fall und um das Hinzufügen von Zeigern im allgemeinen Fall. Daran würde sich nichts ändern; Bei der Frage geht es nicht um das Hinzufügen impliziter Umwandlungen, sondern lediglich um das Ableiten nicht spezifizierter Typen. Tatsächlich bin ich mir nicht sicher, wie irgendjemand das Ententippen daraus verstehen soll; Das ist weitaus weitreichender als dieser Vorschlag.
Beispiel für einen Linux-Kernel: Wie funktioniert das Typecheck-Makro aus dem Linux-Kernel?
_Generic C11-Schlüsselwort: Syntax und Beispielverwendung von _Generic in C11
__auto_type i = 1;
assert(_Generic((i), int: 1, default: 0));
Nichts davon beinhaltet eine Typinferenz im Sinne der Frage (glaube ich). C++11 auto Insbesondere ist dies äußerst trivial und weit entfernt von Typschlussfolgerungen in dem Sinne, wie Menschen aus anderen Gemeinschaften diesen Begriff normalerweise verwenden würden.
– Andreas Rossberg
30. Juli 2015 um 0:23
@AndreasRossberg Können Sie ein minimales Codebeispiel dafür geben, wie eine solche Funktion aussehen würde? en.wikipedia.org/wiki/C%2B%2B11#Type_inference sagt, dass C++11 Typinferenz hat (ich weiß, Wikipedia … =) ) Ich werde einen Blick auf Rust und Hindley-Milner werfen.
– Ciro Santilli OurBigBook.com
30. Juli 2015 um 5:55
Typinferenz beschreibt traditionell die Situation, in der Typinformationen nicht nur von unten nach oben gesammelt, sondern daraus abgeleitet werden Verwendet. Einfaches Beispiel: succ(n) { return n+1 }wenn Sie auf den Typ schließen können n (und somit succ) aus seiner Verwendung in einer Addition.
– Andreas Rossberg
30. Juli 2015 um 6:34
C fördert einige Typen automatisch, was die Sache komplizierter macht. Um jedoch funktionsfähig zu sein, müssen Sie einige zusätzliche Unterschiede zu C in seiner jetzigen Form aufweisen: Derzeit unterstützt der Standard in gewissem Umfang noch ältere K&R-C-Programme, und das erfordert, dass nicht spezifizierte Typen auf besondere Weise behandelt werden. (Siehe z. B. die Regeln für Funktionsparameter bei fehlenden Prototypen. Früher war es auch möglich, Funktionen und Variablen mit anzugeben NEIN Typ, und sie würden standardmäßig verwendet werden (int). (Wie für Variablen? Nur Speicherklasse. static foo;)) Die gesamte veraltete Typbehandlung müsste entfernt werden, bevor ein neuer impliziter Typmechanismus hinzugefügt werden könnte.
Um Typen polymorpher Funktionen abzuleiten, wären dramatische Erweiterungen des C-Typ-Systems erforderlich. Beispiel
Dieser Code muss mit jedem Zeiger auf eine Struktur (oder eine Union) mit a funktionieren next Feld. Sie könnten den Zeilenpolymorphismus ausprobieren, aber zumindest muss Ihr neues Typsystem weitaus ausdrucksvoller sein als das C-Typsystem.
Ein weiteres drängendes Problem ist die Überlastung + Betrieb. Welcher Typ ist standardmäßig aktiviert? Möchten Sie, dass es bei einem beliebigen numerischen Typ wie Haskell oder C++ überladen wird? Wenn ja, weitere große Erweiterungen des Typsystems.
Die größere Lektion ist Tu das nicht. Die Vorzüge von C (als Sprache, abgesehen von den vielen guten APIs, die in C verfügbar sind) sind
Sie haben die volle Kontrolle über die Darstellung Ihrer Daten.
Jeder, der den Quellcode untersucht, kann den Zeit- und Platzaufwand leicht vorhersagen.
Diese Agenda ist nicht wirklich mit Polymorphismus kompatibel, und Polymorphismus ist ein Hauptvorteil der Typinferenz. Wenn Sie Typrückschluss wünschen, wählen Sie eine der vielen guten Sprachen (F#, Haskell, ML), die ihn nativ unterstützen.
Nun, C erlaubt keine Generika, also könnten Sie in diesem Fall (theoretisch) nachsehen, welche Strukturtypen sich im Gültigkeitsbereich befanden, die ein Feld mit dem Namen „next“ hatten, und wenn es zwei oder mehr gab, eine Anmerkung erfordern.
– Deontologe
29. Juni 2011 um 1:11
IMHO, für C selbst Um eine funktionsfähige Sprache zu bleiben, müssen neue Arten der Typendeklaration hinzugefügt werden, die es einem Programmierer ermöglichen würden, „Zahl, deren Wert zwischen 0 und 4294967295 liegt“ von „Mitglied des algebraischen Werterings mit kongruentem Mod 4294967296“ zu unterscheiden. Wenn man Ersteres zu einer beliebigen anderen Zahl hinzufügt, sollten beide auf die bequemste Größe gebracht werden, die groß genug ist, um beide aufzunehmen. Das Hinzufügen einer Zahl zu letzterem sollte ein Ergebnis desselben algebraischen Ringtyps ergeben.
– Superkatze
23. Okt. 2014 um 16:01 Uhr
Es gibt keinen guten Grund, warum ein Prozessor mit 64-Bit-Registern Berechnungen mit Zahlen nicht fördern sollte, um die vollen 64 Bits zu nutzen, aber auf der anderen Seite wird viel Code kaputtgehen, wenn uint32_t hört plötzlich auf, sich wie ein abstrakter algebraischer Ring zu verhalten. Hinzufügen einer Möglichkeit, etwas als Typ zu deklarieren (hypothetische Syntax) unsigned[int 32] wenn Ersteres angemessen ist und unsigned [restrict 32] Wenn letzteres benötigt wird, könnte dies die Robustheit des Codes und die Maschinenunabhängigkeit erheblich verbessern und gleichzeitig den Bedarf an Typumwandlungen verringern.
– Superkatze
23. Okt. 2014 um 16:05 Uhr
Übrigens, was die „vollständige Kontrolle der Daten“ betrifft, ist das bei weitem nicht der Fall, obwohl einige kleine Ergänzungen zur Sprache (wie die oben genannten) das Problem zu 99,9 % beheben würden. Eine weitere Verbesserung, die ich mir wünschen würde, wäre die Möglichkeit, dass Typen das Speicherlayout angeben können [e.g. have unsigned [restrict uint8_t (0:8,8:8,16:8,24:8)] Geben Sie an, dass der 32-Bit-Wert mit vier gespeichert werden muss uint8_t Werte von jeweils 8 Bit, LSB zuerst, unabhängig von der Wortgröße der Maschine; wenn das mit der Wortreihenfolge der Maschine übereinstimmt, großartig; wenn nicht, und wenn der Typ in a verwendet wird union oder…
– Superkatze
23. Okt. 2014 um 16:13
…seine Adresse verwendet wird, müsste der Compiler jede Reihenfolge von Verschiebungen und Speichern verwenden, die zur Verarbeitung des angegebenen Formats erforderlich wäre. Keine große Änderung an der Sprache, aber es würde es ermöglichen, viele Programme so zu schreiben, dass sie prägnanter, robuster und leistungsfähiger sind (wenn eine Big-Endian-Plattform über eine Anweisung zum Austauschen von Bytes in Wörtern verfügt, a Der Compiler für diese Plattform könnte es beim Laden/Speichern des oben genannten Integer-Typs einfacher verwenden, als wenn der Typ mit Code wie gelesen würde value=(p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)))
– Superkatze
23. Okt. 2014 um 16:21
In C gibt es komplizierte Regeln für die Typförderung und -konvertierung, die Menschen verwirrend finden, selbst wenn sie die Typen, mit denen sie es zu tun haben, tatsächlich sehen können. Ich vermute, dass selbst wenn C Typinferenz hätte, es notwendig wäre, bei jeder dritten Funktion Typen zu deklarieren, um lächerliche Fehler zu vermeiden.
Es ist möglich manche Typinferenz in C. Schauen Sie sich dieses Tool an: http://cuda.dcc.ufmg.br/psyche-c. Sie können dort einen Teil eines Programms eingeben und die fehlenden Typdeklarationen werden wiederhergestellt. Wenn wir es zum Beispiel mit einer Variation von Normans Programm füttern:
int length(T p) {
if (p == NULL) return 0;
else return 1 + length(p->next);
}
Ich bin wahrscheinlich langsam; Können Sie ein Beispiel für Ihren ersten Aufzählungspunkt nennen?
– Oliver Charlesworth
28. Juni 2011 um 22:31
Was Sie beschreiben, klingt eher nach einem dynamischen Typsystem. Typinferenz ist insgesamt eine andere Idee.
– Jeff Mercado
28. Juni 2011 um 22:32
Ich glaube nicht, dass ich das Fachwissen habe, um eine vollständige Antwort einzureichen, aber meine Intuition sagt, dass dies durchaus machbar ist, zumindest für eine C-ähnliche Sprache, die vielleicht 90–95 % von C abdeckt. Wir haben bereits eine brauchbare Typinferenz dafür Python (siehe das ShedSkin-Projekt), daher könnte ich mir vorstellen, dass ähnliche Techniken für eine (oder nahezu) Version von C ohne Typdeklaration verwendet werden könnten.
– John Y
28. Juni 2011 um 22:34
@pmg: Typinferenz hat nichts mit bizarren automatischen Typkonvertierungen zu tun. Tatsächlich interagiert es ziemlich schlecht mit ihnen.
– Chuck
28. Juni 2011 um 22:39
@pmg: In C, wie es jetzt ist, handelt es sich um ein undefiniertes Verhalten für Ihren speziellen Fall und um das Hinzufügen von Zeigern im allgemeinen Fall. Daran würde sich nichts ändern; Bei der Frage geht es nicht um das Hinzufügen impliziter Umwandlungen, sondern lediglich um das Ableiten nicht spezifizierter Typen. Tatsächlich bin ich mir nicht sicher, wie irgendjemand das Ententippen daraus verstehen soll; Das ist weitaus weitreichender als dieser Vorschlag.
– Geekosaurier
28. Juni 2011 um 22:40 Uhr