Členy rozšíření pro omezení SRTP

Vložená funkce se staticky vyřešenými parametry typu může vyžadovat člena s konkrétním názvem a podpisem. F# 11.2 umožňuje, aby člen rozšíření v oboru splňoval tento požadavek, i když typ pochází z jiného sestavení.

Pomocí této funkce oddělte obecný algoritmus od adaptérů pro konkrétní typy. Tyto typy nemusíte zabalit ani do nich přidat nominální rozhraní.

Note

V .NET 11.0.100 SDK povolte<LangVersion>preview</LangVersion>, kde kompilátor překládá rozšíření. Viz nastavení jazyka F# 11.2 pro plánovanou skupinou funkcí. Příklady cest a mapování používají knihovnu F# 11.0 a uživatele verze Preview; viz Kompilace knihoven a příjemců.

Přizpůsobení vlastnosti pro sdílený algoritmus

Následující kód aplikace měří cesty reprezentované dvěma existujícími typy rozhraní. Obě cesty mají délku 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

Zdroj knihovny definuje Structural.Adapters a Structural.Algorithms. Adaptéry zpřístupňují stejnou vlastnost souřadnic u typů s různými nativními rozhraními API:

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

Algoritmus žádá o Coordinates vlastnost vracející dvojici hodnot s plovoucí desetinou čárkou:

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))

V každém konkrétním volání kompilátor vybere člena Coordinates označovaného jako určující omezení. Vložený algoritmus přijímá vnitřní nebo rozšiřující vlastnost; prázdné a jednobodové cesty mají nulovou délku.

Sdílení operace mezi typy kolekcí

Omezení rozšíření také podporují statické členy. Modul knihovny Structural.Adapters definuje adaptéry pomocí existujících List.map a Option.map funkcí:

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

V modulu knihovny Structural.Algorithms požádejte Map člena o úplný typ vstupu:

let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
    (^Input :
        (static member Map: ^Input * ('T -> 'U) -> ^Output)
        (source, mapping))

Aplikace pak může sdílet transformaci, změnit typ elementu a vytvořit mapování přes vnořenou kolekci:

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]

Kompilátor kontroluje názvy a typy členů, nikoli rozhraní ani mapování zákonů. Není to polymorfismus vyššího druhu: žádný parametr představuje konstruktor kontejneru, například F<_>.

Rozšíření operátorů

Operátory můžou používat stejný mechanismus. Například definujte operátor mapování pro seznamy a možnosti:

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))

Uživatel se otevře StructuralOperators a použije operátor:

open StructuralOperators

let incremented: int list = [1; 2; 3] |>> ((+) 10)
let length: int option = Some "hello" |>> String.length
// [11; 12; 13] and Some 5

Integrovaná řešení primitivních operátorů si zachovají prioritu pro odpovídající typy. Rozšíření neposkytuje libovolné smíšené rozšíření ani nenahrazuje všechny předdefinované operátory.

Režim F# 11.0 může přijmout běžnou deklaraci operátoru rozšíření s FS1215, ale nemůže ji použít jako řešení SRTP. Projekt operátora používá náhled.

Operátory a kontext řazené kolekce členů

Kolekce pozorování a dvojice nezávisle kumulovaných výsledků se může účastnit stejného kombinovaného protokolu. Následující uživatel definuje svůj obecný algoritmus po otevření adaptérů knihovny:

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"])

Tato rozšíření knihovny dávají operátoru význam pro celá čísla, seznamy a páry:

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 vyžaduje neprázdný seznam, protože používá List.reduce. Operace zachovává pořadí seznamu; kompilátor nevynucuje asociativitu ani neposkytuje identitu pro prázdný vstup.

Nativní hlavičky type ('T1 * 'T2) with a type struct ('T1 * 'T2) with rozšíření odkazů CLR a řazených kolekcí hodnot Oba jsou funkce F# 11.2 a podporují dva až sedm prvků; větší hlavičky vytvářejí FS3915.

Řazené kolekce členů můžou mít také kontext spolu s hodnotou. Následující operátor při použití kontextové funkce připojí stopu:

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)

Volání dot-instance řazené kolekce členů a nativní hlavičky typu funkce, jako type ('a -> 'b) with například nejsou podporovány.

Knihovna složení i spotřebitel používají náhled. Zachovat combineAll ve spotřebiteli: export této redukce může vytvořit FS0193 pro řazenou kolekci ++členů . Samostatné příklady cest a mapování exportují své algoritmy.

Vrácené konstrukce

U operace, která do kontejneru zavádí prostý údaj, může výstupní typ určit, který člen by měl vytvořit:

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"

Poznámka k výsledkům příjemce vybere rozšíření výstupního Return typu. Samotný vstup nemůže zvolit kontejner.

Každý kontejner dodává vlastního člena. Pro přetížení jen pro vrácení jednoho typu použijte samostatný AllowOverloadOnReturnType atribut.

Kompilace knihoven a příjemců

Nastavení jazyka platí pro každý projekt, ne tranzitivně prostřednictvím odkazů. Následující matice používá nastavení sady SDK .NET 11.0.100 pro funkce F# 11.2:

Implementace knihovny Použití spotřebitele Požadavek na verzi Preview
Deklaruje nativní hlavičky rozšíření řazené kolekce členů. Vyřeší operátory rozšíření. Oba potřebují verzi Preview; samotná nová syntaxe záhlaví je ve verzi Preview.
Definuje běžná rozšíření a exportuje nevyřešené omezení vloženého člena. Dodává konkrétní typy, jejichž rozšíření toto omezení řeší. Spotřebitel potřebuje verzi Preview. Knihovna může používat F# 11.0.
Řeší omezení rozšíření interně a zveřejňuje běžné nebo již vyřešené vložené rozhraní API. Volá toto rozhraní API bez dalšího překladu rozšíření. Knihovna potřebuje náhled; uživatel může použít F# 11.0.
Řeší omezení rozšíření a exportů, která příjemce řeší také prostřednictvím rozšíření. Řeší zbývající omezení. Oba potřebují náhled.

Cesta a knihovna mapování používá 11.0; jeho příjemce používá preview. Z adresáře příjemce spusťte dotnet run podporu sady F# 11 SDK.

Obor rozšíření napříč sestaveními

Před definováním závislých vložených funkcí používejte veřejná rozšíření a otevřete jejich modul. U importovaného nevyřešeného omezení také otevřete adaptéry v příjemci; prohlášení výrobce open se nepřenesou. Rozhraní API, které je už vyřešené, nevyžaduje opětovné otevření adaptérů příjemcem.

Odvozování a podpisy

F# 11.2 rozlišení deferuje některé volby, které režim F# 11.0 dělá dříve. Představte si vloženou pomocnou rutinu definovanou příjemcem DateTime :

let inline shift (date: System.DateTime) offset = date + offset

V režimu F# 11.0 může tento formulář odvodit DateTime -> TimeSpan -> DateTime. Pokud je povolené rozlišení rozšíření F# 11.2, může zachovat omezení pro (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Příjemce pak může vybrat veřejné rozšíření v oboru pro jiný typ posunu.

Uživatel nemůže obnovit ztracenou generalitu v knihovně F# 11.0. Kontrola exportovaných podpisů; producent může také potřebovat odvozovat F# 11.2.

Omezení verze Preview a řešení potíží

  • V případě FS0001 nebo FS0193 zkontrolujte nastavení jazyka, veřejnou viditelnost a obor adaptéru.
  • Pokud soubor pozdějšího producenta přeloží příklad Complexcesty , generování kódu může selhat s FS0971, Undefined value 'get_Coordinates'. Toto řešení ponechte s jeho definicemi nebo v samostatném příjemci.
  • Konkurenční adaptéry můžou mít nejednoznačný výběr členů.

Viz také