Im folgenden Codefragment
%Vor%Die Zeile mit dem Kommentar wird nicht kompiliert. Aus irgendeinem Grund kann der Typüberprüfer nicht herausfinden, dass der Typ für p1 myrec1 sein sollte. Liegt es daran, dass dieser Fall der Typinferenz einfach unlösbar ist oder nur eine Einschränkung der F # -Typinferenz ist?
Von hier :
Die Beschriftungen des zuletzt deklarierten Typs haben Vorrang vor diejenigen des zuvor angegebenen Typs
Also wenn du es so machst:
%Vor%dann wird es funktionieren.
Für Ihr Lesevergnügen von hier ( die F # 3.0 Spezifikation):
%Vor%6.3.5 Record Expressions
In diesem Fall ist unser Feldinitialisierer kein einzelner Bezeichner, daher verwendet er "Feldbezeichnerauflösung" von 14.1.9.
Jedes field-initialiseri hat das form field-labeli = expri. Jedes Feld-Labeli ist ein Long-Ident, das muss Auflösen in ein Feld Fi in einem eindeutigen Datensatztyp R wie folgt:
· Wenn field-labeli ein einzelner Bezeichner fld und die Initiale ist Typ ist bekanntermaßen ein Datensatztyp R & lt; em>, ..., & gt; das hat field Fi mit name fld, dann wird die Feldbezeichnung in Fi aufgelöst.
· Wenn field-labeli kein einzelner Bezeichner oder der erste Bezeichner ist type ist ein Variablentyp, dann wird die Feldbezeichnung durch aufgelöst Durchführen einer Feldbeschriftungsauflösung (siehe §14.1) für Feldbezeichnungen. Dies Prozedur führt zu einer Reihe von Feldern FSeti. Jedes Element dieses Sets hat einen entsprechenden Satztyp, was zu einem Datensatz führt Typen RSeti. Die Schnittmenge aller RSeti muss einen einzelnen Datensatz ergeben Geben Sie R ein, und jedes Feld wird dann in das entsprechende Feld in R aufgelöst.
14.1.9 Feldbeschriftungsauflösung
Unser long-ident ist ein FieldLabel, daher wird es mit der in 8.4.2 beschriebenen Tabelle FieldLabels gesucht.
Feldlabelauflösung gibt an, wie Identifikatoren wie Feld1 in {field1 = expr; ... feldN = Ausdruck}. Die Feldkennsatzauflösung führt die folgenden Schritte durch:
1. Suchen Sie alle Felder in allen verfügbaren Typen in der Typen Tabelle und der FieldLabels-Tabelle (§ 8.4.2).
2. Liefert den Satz von Felddeklarationen.
8.4.2 Namensauflösung und Datensatzfeldbeschriftungen
Wie hier erwähnt, wird die FieldLabels-Tabelle in der Namensauflösung für Mitglieder (14.1) verwendet.
Für einen Datensatztyp werden die Datensatzfeldbezeichnungen Feld1 ... FeldN hinzugefügt in die FieldLabels-Tabelle der aktuellen Namensauflösungsumgebung Es sei denn, der Datensatztyp verfügt über das RequireQualifiedAccess-Attribut. Feldbezeichnungen in der FieldLabels - Tabelle spielen eine besondere Rolle in Namensauflösung für Mitglieder (§14.1): Der Typ eines Ausdrucks kann sein von einem Plattenlabel abgeleitet. Zum Beispiel: Geben Sie R = {dx: int; dy: int} sei f x = x.dx // x wird als Typ R bezeichnet In diesem Beispiel Das Nachschlagen .dx wird aufgelöst, um eine Feldsuche zu sein.
14.1.4Name Auflösung in Ausdrücken Dieser Abschnitt scheint etwas unscharf zu sein, aber ich denke, dass er an dieser Stelle die Namensauflösung verwendet. Wie am Ende bemerkt, gib das erste Element zurück, wenn es mehr als eins gibt.
Gegeben ist eine Eingabe long-ident, Umgebung env und eine optionale Anzahl n von die Anzahl der nachfolgenden Typargumente & lt; , ..., & gt ;, Namensauflösung in Expressions berechnet ein Ergebnis, das die Interpretation des long-ident & lt; , ..., & gt; Präfix als Wert oder anderes Ausdruckselement und a Restweg Rest. Wie die Namensauflösung in Expressions verläuft, hängt davon ab ob long-ident ein einzelner Bezeichner ist oder aus mehr besteht als eine Kennung. Wenn long-ident eine einzige ID ist:
1. Suchen Sie in der Tabelle "ExprItems" nach ident. Gib das Ergebnis zurück und leere Pause.
2. Wenn ident nicht in der Tabelle "ExprItems" angezeigt wird, suchen Sie in der Tabelle "Types" nach einer generischen Arity, die mit n übereinstimmt, sofern sie verfügbar ist. Gebe diesen Typ zurück und leere den Rest.
3. Wenn Ident nicht in der Tabelle "ExprItems" oder in der Tabelle "Types" angezeigt wird, schlagen Sie fehl.
...
Enthält der Ausdruck Mehrdeutigkeiten, Namensauflösung in Ausdrücken gibt das erste Ergebnis zurück, das der Prozess generiert.
Der Teil, an dem Sie interessiert sind, ist die letzte Zeile oben: "gibt das erste Ergebnis zurück, das der Prozess generiert".
Dieses Verhalten ist beabsichtigt. Ich kann nicht sagen, ob es eine Beschränkung der Typinferenz von F # oder eine Beschränkung von Typinferenzalgorithmen im Allgemeinen ist; Wenn Sie darüber nachdenken, gibt es zwei Möglichkeiten:
Geben Sie für einen Record-Ausdruck {x = 1; y = 1}
die Felder 'x' und 'y' für den letzten Typ an, um einen der beiden zu deklarieren. Dies ist am einfachsten zu verstehen, und der F # -Compiler implementiert die Inferenz des Datensatztyps.
Versuchen Sie, die beste Anpassung basierend auf den Feldern der Typen im aktuellen Bereich zu ermitteln. (Ich denke, das ist das, worüber du fragst.)
Dieser Algorithmus könnte jedoch zu anderen Problemen führen; Speziell für einen Datensatzausdruck {x = 1; y = 1}
kann der Compiler nicht feststellen, ob Sie einen Ausdruck vom Typ myrec1
erstellen wollten, oder ob Sie einen Ausdruck von myrec2
erstellen wollten und Sie vergessen hatten, dem% einen Wert zuzuweisen. co_de% -Feld.
Was sollte der Compiler auch tun, wenn Sie zwei Typen mit genau denselben Feldern deklarieren? Zum Beispiel, wenn Sie hinzufügen:
%Vor%Mit anderen Worten, gibt es kein kostenloses Mittagessen - Sie können die "Macht" der Typinferenz erhöhen, aber es kostet Sie einige Genauigkeit in der Fehlerdiagnose Sie Holen Sie sich vom Compiler.
Wenn Sie Datensatztypen mit ähnlich benannten Feldern erstellen möchten, können Sie folgendermaßen unterscheiden:
%Vor%Da es keine Typ-Annotationen gibt, leitet der Compiler den Record-Typ von den Labels ab, aber diejenigen des ersten Typs unterscheiden sich nicht ausreichend von den Labels des zweiten, so dass letzterer Vorrang hat und p1 vom Typ myrec2 ist. Verwenden Sie verschiedene Beschriftungen, um Typanmerkungen zu vermeiden und das erwartete Inferenzverhalten zu erhalten:
%Vor%Tags und Links f# type-inference