Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Een inlinefunctie met statisch opgeloste typeparameters kan een lid met een bepaalde naam en handtekening vereisen. Met F# 11.2 kan een uitbreidingslid binnen het bereik aan die vereiste voldoen, zelfs wanneer het type afkomstig is van een andere assembly.
Gebruik deze mogelijkheid om een algemeen algoritme te scheiden van adapters voor bepaalde typen. U hoeft deze typen niet te verpakken of er een nominale interface aan toe te voegen.
Note
Schakel in de .NET 11.0.100 SDK <LangVersion>preview</LangVersion> in waar de compiler extensies oplost. Zie de taalinstellingen voor F# 11.2 voor de geplande functieband. Het pad en de toewijzingsvoorbeelden maken gebruik van een F# 11.0-bibliotheek en een preview-consument; zie Compileerbibliotheken en consumenten.
Een eigenschap aanpassen voor een gedeeld algoritme
De volgende toepassingscode meet paden die worden vertegenwoordigd door twee bestaande frameworktypen. Beide paden hebben een lengte van 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
De bibliotheekbron definieert Structural.Adapters en Structural.Algorithms. De adapters maken dezelfde coördinateneigenschap beschikbaar voor typen met verschillende systeemeigen API's:
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
Het algoritme vraagt om een Coordinates eigenschap die een paar drijvendekommawaarden retourneert:
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))
Bij elke concrete aanroep selecteert de compiler een Coordinates lid, de zogenaamde getuige van de beperking. Het inline-algoritme accepteert een intrinsieke of uitbreidingseigenschap; lege paden en paden met één punt hebben lengte nul.
Een bewerking delen tussen verzamelingstypen
Uitbreidingsbeperkingen ondersteunen ook statische leden. De module van Structural.Adapters de bibliotheek definieert adapters met behulp van de bestaande List.map functies:Option.map
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
Vraag in de module van Structural.Algorithms de bibliotheek een Map lid aan voor het volledige invoertype:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
De toepassing kan vervolgens een transformatie delen, het elementtype wijzigen en toewijzing opstellen via een geneste verzameling:
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]
De compiler controleert lidnamen en -typen, niet interfaces of toewijzingswetten. Dit is geen polymorfisme met een hogere soort: geen parameter vertegenwoordigt een containerconstructor zoals F<_>.
Operatorextensies
Operators kunnen hetzelfde mechanisme gebruiken. Definieer bijvoorbeeld een toewijzingsoperator voor lijsten en opties:
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))
Een consument wordt geopend StructuralOperators en gebruikt de 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
Ingebouwde primitieve operatoroplossingen behouden prioriteit voor overeenkomende typen. Een extensie biedt geen willekeurige mixed-numerieke widening of vervangt alle ingebouwde operators.
De F# 11.0-modus kan een gewone uitbreidingsoperatordeclaratie met FS1215 accepteren, maar kan deze niet gebruiken als een SRTP-oplossing. Het operatorproject maakt gebruik van preview.
Tuple-operators en -context
Een verzameling waarnemingen en een paar onafhankelijk samengevoegde resultaten kunnen deelnemen aan hetzelfde combinatieprotocol. De volgende consument definieert het algemene algoritme na het openen van de adapters van de bibliotheek:
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"])
Deze bibliotheekextensies geven de operator een betekenis voor gehele getallen, lijsten en paren:
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 vereist een lege lijst omdat deze wordt gebruikt List.reduce. De bewerking behoudt de volgorde van de lijst; De compiler dwingt geen associativiteit af of levert een identiteit voor een lege invoer.
Systeemeigen headers type ('T1 * 'T2) with en type struct ('T1 * 'T2) with breidt CLR-verwijzing en waarde-tuples uit. Beide zijn F# 11.2-functies en ondersteunen twee tot en met zeven elementen; grotere headers produceren FS3915.
Tuples kunnen ook context naast een waarde dragen. De volgende operator voegt een trail toe tijdens het toepassen van een contextuele functie:
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)
Aanroepen van tuple-dot-instances en systeemeigen functie-type headers, zoals type ('a -> 'b) with worden niet ondersteund.
De samenstellingsbibliotheek en de consument maken beide gebruik van preview. Houd combineAll de consument bij: exporteren die vermindering kan FS0193 voor tuple ++produceren. De afzonderlijke pad- en toewijzingsvoorbeelden exporteren hun algoritmen.
Retour gerichte constructie
Voor een bewerking die een gewone waarde in een container introduceert, kan het uitvoertype bepalen welk lid deze moet samenstellen:
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"
De aantekening van het resultaat van de consument selecteert de extensie van Return het uitvoertype. De invoer alleen kan geen container kiezen.
Elke container levert een eigen lid. Gebruik het afzonderlijke AllowOverloadOnReturnType kenmerk voor overbelastingen met alleen retourneert voor één type.
Bibliotheken en consumenten compileren
Taalinstellingen zijn van toepassing per project, niet transitief via verwijzingen. In de volgende matrix worden de instellingen van de .NET 11.0.100 SDK gebruikt voor F# 11.2-functies:
| Bibliotheek-implementatie | Consumentengebruik | Preview-vereiste |
|---|---|---|
| Declareert systeemeigen tuple-extension-headers. | Lost de extensieoperators op. | Beide hebben preview nodig; de nieuwe headersyntaxis zelf is een voorbeeld. |
| Hiermee definieert u gewone extensies en exporteert u een onopgeloste inlinelidbeperking. | Levert betontypen waarvan de uitbreidingen die beperking oplossen. | Voorbeeld van consumentenbehoeften. De bibliotheek kan F# 11.0 gebruiken. |
| Lost een uitbreidingsbeperking intern op en maakt een gewone of al opgeloste inline-API beschikbaar. | Roept die API aan zonder verdere uitbreidingsresolutie. | Voorbeeld van bibliotheekbehoeften; de consument kan F# 11.0 gebruiken. |
| Hiermee lost u uitbreidingen en exportbeperkingen op die de consument ook oplost via extensies. | Hiermee worden de resterende beperkingen opgelost. | Beide hebben preview nodig. |
Het pad en de toewijzingsbibliotheek gebruikt 11.0; de consument gebruikt preview. Voer dotnet run vanuit de consumentendirectory uit met een ondersteunende F# 11 SDK.
Uitbreidingsbereik voor assembly's
Gebruik openbare extensies en open de module voordat u afhankelijke inlinefuncties definieert. Voor een geïmporteerde niet-opgeloste beperking opent u ook de adapters in de consument; de aangiften van open de producent worden niet overgedragen. Voor een al opgeloste API hoeft de consument de adapters niet opnieuw te openen.
Deductie en handtekeningen
Met F# 11.2-resolutie worden sommige keuzes uitgesteld die de F# 11.0-modus eerder maakt. Overweeg een inline helper die is gedefinieerd met een DateTime ontvanger:
let inline shift (date: System.DateTime) offset = date + offset
In de F# 11.0-modus kan dit formulier afleiden DateTime -> TimeSpan -> DateTime. Als F# 11.2-extensieresolutie is ingeschakeld, kan deze een beperking voor (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Een consument kan vervolgens een openbare uitbreiding binnen het bereik selecteren voor een ander offsettype.
Een consument kan de algemeenheid die verloren gaat in een F# 11.0-bibliotheek niet herstellen. Geëxporteerde handtekeningen controleren; De producent moet mogelijk ook F# 11.2 deductie inschakelen.
Preview-beperkingen en probleemoplossing
- Voor FS0001 of FS0193 controleert u de taalinstellingen, de openbare zichtbaarheid en het adapterbereik.
- Als een later producerbestand het padvoorbeeld oplost
Complex, kan het genereren van code mislukken met FS0971,Undefined value 'get_Coordinates'. Bewaar die resolutie met de definities ervan of in een afzonderlijke consument. - Concurrerende adapters kunnen lidselectie dubbelzinnig maken.