Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Funkcja śródliniowa ze statycznie rozpoznanymi parametrami typu może wymagać elementu członkowskiego o określonej nazwie i podpisie. Język F# 11.2 umożliwia członkowi rozszerzenia w zakresie spełnianie tego wymagania, nawet jeśli typ pochodzi z innego zestawu.
Ta funkcja umożliwia oddzielenie algorytmu ogólnego od kart dla określonych typów. Nie musisz opakowować tych typów ani dodawać do nich nominalnego interfejsu.
Note
W zestawie SDK .NET 11.0.100 włącz<LangVersion>preview</LangVersion>, gdzie kompilator rozpoznaje rozszerzenia. Zobacz Ustawienia języka F# 11.2 dla planowanego pasma funkcji. Przykłady ścieżki i mapowania używają biblioteki języka F# 11.0 i odbiorcy wersji zapoznawczej; Zobacz Kompilowanie bibliotek i odbiorców.
Dostosowywanie właściwości dla algorytmu współużytkowanego
Poniższy kod aplikacji mierzy ścieżki reprezentowane przez dwa istniejące typy platform. Obie ścieżki mają długość 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
Źródło biblioteki definiuje Structural.Adapters element i Structural.Algorithms. Karty uwidacznia tę samą właściwość współrzędnych dla typów z różnymi natywnymi interfejsami 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
Algorytm pyta o Coordinates właściwość zwracającą parę wartości zmiennoprzecinkowych:
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))
W każdym konkretnym wywołaniu kompilator wybiera element Coordinates członkowski, nazywany monitorem ograniczenia. Algorytm wbudowany akceptuje właściwość wewnętrzną lub rozszerzenie; puste i jednopunktowe ścieżki mają długość zero.
Udostępnianie operacji między typami kolekcji
Ograniczenia rozszerzeń obsługują również statyczne elementy członkowskie. Moduł biblioteki Structural.Adapters definiuje karty przy użyciu istniejących List.map funkcji i 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
W module biblioteki Structural.Algorithms zażądaj Map elementu członkowskiego w pełnym typie danych wejściowych:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Aplikacja może następnie udostępnić przekształcenie, zmienić typ elementu i utworzyć mapowanie na zagnieżdżoną kolekcję:
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]
Kompilator sprawdza nazwy i typy elementów członkowskich, a nie interfejsy ani prawa mapowania. Nie jest to polimorfizm bardziej uprzejmy: żaden parametr nie reprezentuje konstruktora kontenera, takiego jak F<_>.
Rozszerzenia operatorów
Operatory mogą używać tego samego mechanizmu. Na przykład zdefiniuj operator mapowania dla list i opcji:
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żytkownik zostanie otwarty StructuralOperators i użyje operatora :
open StructuralOperators
let incremented: int list = [1; 2; 3] |>> ((+) 10)
let length: int option = Some "hello" |>> String.length
// [11; 12; 13] and Some 5
Wbudowane rozwiązania operatorów pierwotnych zachowują priorytet dla pasujących typów. Rozszerzenie nie zapewnia dowolnego rozszerzenia z liczbą mieszaną ani nie zastępuje wszystkich wbudowanych operatorów.
Tryb F# 11.0 może akceptować zwykłą deklarację operatora rozszerzenia z fs1215, ale nie może używać go jako rozwiązania SRTP. Projekt operatora używa wersji zapoznawczej.
Operatory krotki i kontekst
Kolekcja obserwacji i para niezależnie skumulowanych wyników może uczestniczyć w tym samym protokole kombinacji. Następujący użytkownik definiuje swój algorytm ogólny po otwarciu kart biblioteki:
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"])
Te rozszerzenia bibliotek zapewniają operatorowi znaczenie dla liczb całkowitych, list i par:
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 wymaga listy nonempty, ponieważ używa List.reduce. Operacja zachowuje kolejność listy; kompilator nie wymusza asocjacji ani nie dostarcza tożsamości dla pustych danych wejściowych.
Nagłówki type ('T1 * 'T2) with natywne i type struct ('T1 * 'T2) with rozszerzają odwołania CLR i krotki wartości. Oba są funkcjami języka F# 11.2 i obsługują dwa do siedmiu elementów; większe nagłówki generują fs3915.
Krotki mogą również przenosić kontekst obok wartości. Następujący operator dołącza ślad podczas stosowania funkcji kontekstowej:
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)
Wywołania krotki dot-instance i natywne nagłówki typu funkcji, takie jak type ('a -> 'b) with nie są obsługiwane.
Biblioteka kompozycji i użytkownik używają wersji zapoznawczej. Zachowaj combineAll w konsumentu: eksportowanie tej redukcji może produkować FS0193 dla krotki ++. Oddzielne przykłady ścieżki i mapowania eksportują swoje algorytmy.
Konstrukcja skierowana do powrotu
W przypadku operacji, która wprowadza zwykłą wartość do kontenera, typ danych wyjściowych może określić, który element członkowski powinien go skonstruować:
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"
Adnotacja wyniku odbiorcy wybiera rozszerzenie typu Return danych wyjściowych. Dane wejściowe nie mogą wybrać kontenera.
Każdy kontener dostarcza własny element członkowski. W przypadku przeciążeń tylko zwracanych dla pojedynczego typu należy użyć oddzielnego AllowOverloadOnReturnType atrybutu.
Kompilowanie bibliotek i użytkowników
Ustawienia języka są stosowane dla każdego projektu, a nie przechodnio za pomocą odwołań. Poniższa macierz używa ustawień zestawu SDK .NET 11.0.100 dla funkcji języka F# 11.2:
| Implementacja biblioteki | Użycie konsumenta | Wymaganie dotyczące wersji zapoznawczej |
|---|---|---|
| Deklaruje natywne nagłówki rozszerzeń krotki. | Rozwiązuje swoje operatory rozszerzeń. | Obie wymagają wersji zapoznawczej; nowa składnia nagłówka jest dostępna w wersji zapoznawczej. |
| Definiuje zwykłe rozszerzenia i eksportuje nierozwiązane ograniczenie wbudowanego elementu członkowskiego. | Dostarcza konkretne typy, których rozszerzenia rozwiązuje to ograniczenie. | Użytkownik potrzebuje wersji zapoznawczej. Biblioteka może używać języka F# 11.0. |
| Rozwiązuje ograniczenie rozszerzenia wewnętrznie i uwidacznia zwykły lub już rozwiązany wbudowany interfejs API. | Wywołuje ten interfejs API bez dalszego rozwiązywania problemów z rozszerzeniem. | Biblioteka wymaga wersji zapoznawczej; użytkownik może używać języka F# 11.0. |
| Rozwiązuje ograniczenia rozszerzeń i eksportów, które użytkownik rozwiązuje również za pomocą rozszerzeń. | Rozwiązuje pozostałe ograniczenia. | Obie muszą mieć wersję zapoznawcza. |
Biblioteka ścieżek i mapowania używa 11.0elementu ; jego odbiorca używa elementu preview. W katalogu odbiorców uruchom dotnet run polecenie z pomocniczym zestawem SDK języka F# 11.
Zakres rozszerzenia między zestawami
Użyj rozszerzeń publicznych i otwórz swój moduł przed zdefiniowaniem zależnych funkcji wbudowanych. W przypadku zaimportowanego ograniczenia nierozwiązanego otwórz również karty u konsumenta; deklaracje producenta open nie są przenoszone. Interfejs API, który został już rozwiązany, nie wymaga ponownego otwarcia kart przez użytkownika.
Wnioskowanie i podpisy
Rozdzielczość języka F# 11.2 odchyli niektóre opcje, które tryb F# 11.0 wykonuje wcześniej. Rozważ użycie wbudowanego pomocnika zdefiniowanego za pomocą odbiornika DateTime :
let inline shift (date: System.DateTime) offset = date + offset
W trybie F# 11.0 ten formularz może wywnioskować DateTime -> TimeSpan -> DateTime. Po włączeniu rozpoznawania rozszerzeń języka F# 11.2 można zachować ograniczenie dla programu (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Użytkownik może następnie wybrać publiczne rozszerzenie zakresu dla innego typu przesunięcia.
Użytkownik nie może odzyskać uogólnienia utraconego w bibliotece języka F# 11.0. Sprawdzanie wyeksportowanych podpisów; Producent może również wymagać włączenia wnioskowania F# 11.2.
Ograniczenia wersji zapoznawczej i rozwiązywanie problemów
- W przypadku fs0001 lub FS0193 sprawdź ustawienia języka, widoczność publiczną i zakres adaptera.
- Jeśli późniejszy plik producenta rozpozna przykład ścieżki dla
Complex, generowanie kodu może zakończyć się niepowodzeniem z FS0971,Undefined value 'get_Coordinates'. Zachowaj to rozwiązanie przy użyciu jego definicji lub w osobnym odbiorcy. - Konkurencyjne karty mogą sprawić, że wybór elementu członkowskiego będzie niejednoznaczny.