Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Eine Inlinefunktion mit statisch aufgelösten Typparametern kann ein Element mit einem bestimmten Namen und einer bestimmten Signatur erfordern. F# 11.2 ermöglicht es einem erweiterungsinternen Member, diese Anforderung zu erfüllen, auch wenn der Typ aus einer anderen Assembly stammt.
Verwenden Sie diese Funktion, um einen generischen Algorithmus von Adaptern für bestimmte Typen zu trennen. Sie müssen diese Typen nicht umschließen oder eine nominale Schnittstelle hinzufügen.
Note
Aktivieren Sie <LangVersion>preview</LangVersion> im .NET 11.0.100 SDK, wo der Compiler Erweiterungen aufgelöst. Weitere Informationen finden Sie unter F# 11.2-Spracheinstellungen für das geplante Featureband. Die Pfad- und Zuordnungsbeispiele verwenden eine F# 11.0-Bibliothek und einen Vorschau-Consumer. siehe Kompilierungsbibliotheken und Consumer.
Anpassen einer Eigenschaft für einen freigegebenen Algorithmus
Der folgende Anwendungscode misst Pfade, die durch zwei vorhandene Frameworktypen dargestellt werden. Beide Pfade weisen eine Länge von 8:
open System.Numerics
open Structural.Adapters
open Structural.Algorithms
let complexRoute =
[ Complex(0.0, 0.0); Complex(3.0, 4.0); Complex(6.0, 4.0) ]
let vectorRoute =
[ Vector2(0.0f, 0.0f); Vector2(3.0f, 4.0f); Vector2(6.0f, 4.0f) ]
printn (pathLength complexRoute) // 8
printn (pathLength vectorRoute) // 8
Die BibliotheksquelleStructural.Adapters definiert und Structural.Algorithms. Die Adapter machen dieselbe Koordinateneigenschaft für Typen mit unterschiedlichen systemeigenen APIs verfügbar:
type System.Numerics.Complex with
member point.Coordinates = point.Real, point.Imaginary
type System.Numerics.Vector2 with
member point.Coordinates = float point.X, float point.Y
Der Algorithmus fragt nach einer Coordinates Eigenschaft, die ein Paar Gleitkommawerte zurückgibt:
let inline coordinates (point: ^Point) : float * float =
(^Point : (member Coordinates: float * float) point)
let inline pathLength (points: ^Point list) =
points
|> List.pairwise
|> List.sumBy (fun (a, b) ->
let ax, ay = coordinates a
let bx, by = coordinates b
let dx, dy = bx - ax, by - ay
sqrt (dx * dx + dy * dy))
Bei jedem konkreten Aufruf wählt der Compiler ein Coordinates Element aus, das als Zeuge der Einschränkung bezeichnet wird. Der Inlinealgorithmus akzeptiert entweder eine systeminterne oder Erweiterungseigenschaft; Leere und Einpunktpfade weisen die Länge Null auf.
Freigeben eines Vorgangs über Sammlungstypen hinweg
Erweiterungseinschränkungen unterstützen auch statische Member. Das Modul der Bibliothek Structural.Adapters definiert Adapter mithilfe der vorhandenen List.map und Option.map Funktionen:
type List<'T> with
static member Map(source: 'T list, mapping: 'T -> 'U) : 'U list =
List.map mapping source
type Option<'T> with
static member Map(source: 'T option, mapping: 'T -> 'U) : 'U option =
Option.map mapping source
Fordern Sie im Structural.Algorithms Modul der Bibliothek ein Map Mitglied für den vollständigen Eingabetyp an:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Die Anwendung kann dann eine Transformation freigeben, den Elementtyp ändern und die Zuordnung zum Verfassen über eine geschachtelte Auflistung verfassen:
let inline cleanNames values =
map (fun (name: string) -> name.Trim().ToUpperInvariant()) values
let batch: string list = cleanNames [ " Ada "; " grace " ]
let optional: string option = cleanNames (Some " linus ")
let missing: string option = cleanNames (None: string option)
let lengths: int list = map String.length batch
let nested: int option list = map (map String.length) [ Some "abc"; None ]
printfn "%A; %A; %A" batch optional missing
printfn "%A; %A" lengths nested
// ["ADA"; "GRACE"]; Some "LINUS"; None
// [3; 5]; [Some 3; None]
Der Compiler überprüft Membernamen und Typen, nicht Schnittstellen oder Zuordnungsgesetze. Dies ist kein polymorpher Wert mit höherer Art: Kein Parameter stellt einen Containerkonstruktor dar, z F<_>. B. .
Operatorerweiterungen
Operatoren können denselben Mechanismus verwenden. Definieren Sie beispielsweise einen Zuordnungsoperator für Listen und Optionen:
type List<'T> with
static member (|>>) (source: 'T list, mapping: 'T -> 'U) =
List.map mapping source
type Option<'T> with
static member (|>>) (source: 'T option, mapping: 'T -> 'U) =
Option.map mapping source
let inline (|>>) (source: ^Input) (mapping: 'T -> 'U) : ^Output =
(^Input :
(static member (|>>): ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Ein Verbraucher öffnet StructuralOperators und verwendet den Operator:
open StructuralOperators
let incremented: int list = [1; 2; 3] |>> ((+) 10)
let length: int option = Some "hello" |>> String.length
// [11; 12; 13] and Some 5
Integrierte primitive Operatorlösungen behalten die Priorität für übereinstimmende Typen bei. Eine Erweiterung bietet keine beliebige gemischte numerische Erweiterung oder ersetzt alle integrierten Operatoren.
Der F# 11.0-Modus kann eine normale Erweiterungsoperatordeklaration mit FS1215 akzeptieren, kann aber nicht als SRTP-Lösung verwendet werden. Das Operatorprojekt verwendet die Vorschau.
Tupeloperatoren und Kontext
Eine Sammlung von Beobachtungen und ein Paar unabhängig gesammelter Ergebnisse können am gleichen Kombinationsprotokoll teilnehmen. Der folgende Consumer definiert seinen generischen Algorithmus nach dem Öffnen der Adapter der Bibliothek:
open Composition.Adapters
open Composition.Algorithms
let inline combineAll (batches: ^T list) : ^T =
List.reduce (++) batches
let observations = combineAll [["temperature"]; ["pressure"; "humidity"]]
let summary = combineAll [(2, ["temperature"]); (3, ["pressure"; "humidity"])]
printfn "%A" observations
printfn "%A" summary
// ["temperature"; "pressure"; "humidity"]
// (5, ["temperature"; "pressure"; "humidity"])
Diese Bibliothekserweiterungen geben dem Operator eine Bedeutung für ganze Zahlen, Listen und Paare:
type System.Int32 with
static member (++) (left: int, right: int) = left + right
type List<'T> with
static member (++) (left: 'T list, right: 'T list) = left @ right
type ('T1 * 'T2) with
static member inline (++) ((a, b), (c, d)) = (a ++ c, b ++ d)
combineAll erfordert eine nicht entsentliche Liste, da sie verwendet List.reducewird. Der Vorgang behält die Listenreihenfolge bei; der Compiler erzwingt keine Zuordnung oder stellt keine Identität für eine leere Eingabe zur Verfügung.
Native Header type ('T1 * 'T2) with und type struct ('T1 * 'T2) with Erweitern von CLR-Verweisen und Wert-Tupeln. Beide sind F# 11.2-Features und unterstützen zwei bis sieben Elemente. größere Header erzeugen FS3915.
Tupel können auch Kontext neben einem Wert enthalten. Der folgende Operator fügt beim Anwenden einer Kontextfunktion einen Trail an:
let priced =
(["catalogue price"], fun units -> decimal units * 12m)
<*> (["validated quantity"], 3)
printfn "%A" priced
// (["catalogue price"; "validated quantity"], 36M)
type ('Context * 'Value) with
static member inline (<*>) ((before, mapping), (after, value)) =
(before ++ after, mapping value)
type struct ('Context * 'Value) with
static member inline (<*>) (struct (before, mapping), struct (after, value)) =
struct (before ++ after, mapping value)
Tupel-Punktinstanzaufrufe und systemeigene Funktionstypheader, z type ('a -> 'b) with . B. werden nicht unterstützt.
Die Kompositionsbibliothek und der Consumer verwenden beide die Vorschau. Bleiben Sie combineAll im Verbraucher: Export dieser Reduzierung kann FS0193 für Tupel ++produzieren. Die separaten Pfad- und Zuordnungsbeispiele exportieren ihre Algorithmen.
Rückgabegesteuerte Konstruktion
Bei einem Vorgang, der einen einfachen Wert in einen Container einführt, kann der Ausgabetyp identifizieren, welches Element es erstellen soll:
type List<'T> with
static member Return(value: 'T) : 'T list = [value]
type Option<'T> with
static member Return(value: 'T) : 'T option = Some value
let inline singleton (value: 'T) : ^Output =
(^Output : (static member Return: 'T -> ^Output) value)
let oneBatch: string list = singleton "ready"
let oneOptional: string option = singleton "ready"
// ["ready"] and Some "ready"
Die Ergebnisanmerkung des Consumers wählt die Erweiterung des Ausgabetyps Return aus. Die Eingabe allein kann keinen Container auswählen.
Jeder Container liefert ein eigenes Mitglied. Verwenden Sie für rückgabegeschützte Überladungen für einen einzelnen Typ das separate AllowOverloadOnReturnType Attribut.
Kompilieren von Bibliotheken und Consumern
Spracheinstellungen gelten pro Projekt, nicht transitiv durch Verweise. Die folgende Matrix verwendet die Einstellungen des .NET 11.0.100 SDK für F# 11.2-Features:
| Bibliotheksimplementierung | Nutzung von Verbrauchern | Vorschauanforderung |
|---|---|---|
| Deklariert systemeigene Tupelerweiterungsheader. | Löst ihre Erweiterungsoperatoren auf. | Beide benötigen eine Vorschau; Die neue Headersyntax selbst ist eine Vorschau. |
| Definiert normale Erweiterungen und exportiert eine nicht aufgelöste Inlinemembeschränkung. | Liefert konkrete Typen, deren Erweiterungen diese Einschränkung lösen. | Consumer needs preview. Die Bibliothek kann F# 11.0 verwenden. |
| Löst eine Erweiterungseinschränkung intern und macht eine normale oder bereits aufgelöste Inline-API verfügbar. | Ruft diese API ohne weitere Erweiterungsauflösung auf. | Bibliothek benötigt Vorschau; der Consumer kann F# 11.0 verwenden. |
| Löst Erweiterungen und exportiert Einschränkungen, die der Verbraucher auch durch Erweiterungen auflösen kann. | Löst verbleibende Einschränkungen auf. | Beide benötigen eine Vorschau. |
Die Pfad- und Zuordnungsbibliothek verwendet 11.0; der Verbraucher verwendet preview. Führen Sie dotnet run aus dem Consumerverzeichnis mit einem unterstützenden F# 11 SDK aus.
Erweiterungsbereich über Assemblys hinweg
Verwenden Sie öffentliche Erweiterungen, und öffnen Sie ihr Modul, bevor Sie abhängige Inlinefunktionen definieren. Für eine importierte ungelöste Einschränkung öffnen Sie auch die Adapter im Verbraucher; die Erklärungen des Herstellers open werden nicht übertragen. Eine bereits aufgelöste API erfordert nicht, dass der Verbraucher seine Adapter erneut öffnet.
Rückschluss und Signaturen
F# 11.2-Auflösung verzögert einige Optionen, die der F#11.0-Modus früher vorgibt. Betrachten Sie einen Inlinehilfsprogramm, der mit einem DateTime Empfänger definiert ist:
let inline shift (date: System.DateTime) offset = date + offset
Im F# 11.0-Modus kann dieses Formular ableiten DateTime -> TimeSpan -> DateTime. Wenn die F# 11.2-Erweiterungsauflösung aktiviert ist, kann sie eine Einschränkung für (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Ein Consumer kann dann eine öffentliche erweiterung im Gültigkeitsbereich für einen anderen Offsettyp auswählen.
Ein Verbraucher kann in einer F# 11.0-Bibliothek verloren gegangene Allgemeinheit nicht wiederherstellen. Überprüfen exportierter Signaturen; Der Produzent benötigt möglicherweise auch F# 11.2 Inference aktiviert.
Vorschaubeschränkungen und Problembehandlung
- Überprüfen Sie für FS0001 oder FS0193 Spracheinstellungen, öffentliche Sichtbarkeit und Adapterbereich.
- Wenn eine spätere Produzentendatei das Pfadbeispiel für
Complex, code generation can fail with FS0971,Undefined value 'get_Coordinates'. Behalten Sie diese Lösung mit ihren Definitionen oder in einem separaten Verbraucher bei. - Konkurrierende Adapter können eine mehrdeutige Memberauswahl treffen.