Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Fungsi sebaris dengan parameter jenis yang diselesaikan secara statis dapat memerlukan anggota dengan nama dan tanda tangan tertentu. F# 11.2 memungkinkan anggota ekstensi dalam cakupan memenuhi persyaratan tersebut, bahkan ketika jenis berasal dari rakitan lain.
Gunakan kemampuan ini untuk memisahkan algoritma generik dari adaptor untuk jenis tertentu. Anda tidak perlu membungkus jenis tersebut atau menambahkan antarmuka nominal ke dalamnya.
Note
Di .NET 11.0.100 SDK, aktifkan <LangVersion>preview</LangVersion> tempat kompilator menyelesaikan ekstensi. Lihat Pengaturan bahasa F# 11.2 untuk pita fitur yang direncanakan. Contoh jalur dan pemetaan menggunakan pustaka F# 11.0 dan konsumen pratinjau; lihat Mengkompilasi pustaka dan konsumen.
Mengadaptasi properti untuk algoritma bersama
Kode aplikasi berikut mengukur jalur yang diwakili oleh dua jenis kerangka kerja yang ada. Kedua jalur memiliki panjang 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
Sumber pustaka menentukan Structural.Adapters dan Structural.Algorithms. Adaptor mengekspos properti koordinat yang sama pada jenis dengan API asli yang berbeda:
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
Algoritma meminta properti yang Coordinates mengembalikan sepasang nilai floating-point:
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))
Pada setiap panggilan konkret, pengkompilasi memilih Coordinates anggota, yang disebut saksi batasan. Algoritma sebaris menerima properti intrinsik atau ekstensi; jalur kosong dan titik tunggal memiliki panjang nol.
Berbagi operasi di seluruh jenis koleksi
Batasan ekstensi juga mendukung anggota statis. Modul pustaka Structural.Adapters mendefinisikan adaptor menggunakan fungsi dan Option.map yang adaList.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
Dalam modul pustaka Structural.Algorithms , minta Map anggota pada jenis input lengkap:
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
Aplikasi kemudian dapat berbagi transformasi, mengubah jenis elemen, dan menyusun pemetaan melalui koleksi berlapis:
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]
Pengkompilasi memeriksa nama dan jenis anggota, bukan antarmuka atau undang-undang pemetaan. Ini bukan polimorfisme yang lebih tinggi: tidak ada parameter yang mewakili konstruktor kontainer seperti F<_>.
Ekstensi operator
Operator dapat menggunakan mekanisme yang sama. Misalnya, tentukan operator pemetaan untuk daftar dan opsi:
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))
Konsumen membuka StructuralOperators dan menggunakan 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
Solusi operator primitif bawaan mempertahankan prioritas untuk jenis yang cocok. Ekstensi tidak menyediakan pelesiran campuran-numerik arbitrer atau mengganti semua operator bawaan.
Mode F# 11.0 dapat menerima deklarasi operator ekstensi biasa dengan FS1215, tetapi tidak dapat menggunakannya sebagai solusi SRTP. Proyek operator menggunakan pratinjau.
Operator dan konteks tuple
Kumpulan pengamatan dan sepasang hasil akumulasi independen dapat berpartisipasi dalam protokol kombinasi yang sama. Konsumen berikut mendefinisikan algoritma generiknya setelah membuka adaptor pustaka:
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"])
Ekstensi pustaka ini memberi operator arti untuk bilangan bulat, daftar, dan pasangan:
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 memerlukan daftar yang tidak ada karena menggunakan List.reduce. Operasi mempertahankan urutan daftar; pengkompilasi tidak memberlakukan associativity atau menyediakan identitas untuk input kosong.
Header type ('T1 * 'T2) with asli dan type struct ('T1 * 'T2) with perluas referensi CLR dan tuple nilai. Keduanya adalah fitur F# 11.2 dan mendukung dua hingga tujuh elemen; header yang lebih besar menghasilkan FS3915.
Tuple juga dapat membawa konteks bersama nilai. Operator berikut menambahkan jejak saat menerapkan fungsi kontekstual:
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)
Panggilan instans titik tuple dan header jenis fungsi asli seperti type ('a -> 'b) with tidak didukung.
Pustaka komposisi dan konsumen keduanya menggunakan pratinjau. Simpan combineAll di konsumen: mengekspor pengurangan tersebut dapat menghasilkan FS0193 untuk tuple ++. Jalur terpisah dan contoh pemetaan mengekspor algoritmanya.
Konstruksi yang diarahkan kembali
Untuk operasi yang memperkenalkan nilai biasa ke dalam kontainer, jenis output dapat mengidentifikasi anggota mana yang harus membangunnya:
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"
Anotasi hasil konsumen memilih ekstensi jenis Return output. Input saja tidak dapat memilih kontainer.
Setiap kontainer memasok anggotanya sendiri. Untuk kelebihan beban hanya-kembali pada satu jenis, gunakan atribut terpisahAllowOverloadOnReturnType.
Mengkompilasi pustaka dan konsumen
Pengaturan bahasa berlaku per proyek, tidak secara transitif melalui referensi. Matriks berikut menggunakan pengaturan .NET 11.0.100 SDK untuk fitur F# 11.2:
| Implementasi pustaka | Penggunaan konsumen | Persyaratan pratinjau |
|---|---|---|
| Mendeklarasikan header ekstensi tuple asli. | Menyelesaikan operator ekstensi mereka. | Keduanya membutuhkan pratinjau; sintaks header baru itu sendiri adalah pratinjau. |
| Menentukan ekstensi biasa dan mengekspor batasan anggota sebaris yang belum terselesaikan. | Memasok jenis beton yang ekstensinya menyelesaikan batasan tersebut. | Konsumen membutuhkan pratinjau. Pustaka dapat menggunakan F# 11.0. |
| Memecahkan batasan ekstensi secara internal dan mengekspos API sebaris biasa atau sudah diselesaikan. | Memanggil API tersebut tanpa resolusi ekstensi lebih lanjut. | Pustaka membutuhkan pratinjau; konsumen dapat menggunakan F# 11.0. |
| Menyelesaikan batasan ekstensi dan ekspor yang juga diselesaikan konsumen melalui ekstensi. | Mengatasi batasan yang tersisa. | Keduanya membutuhkan pratinjau. |
Jalur dan pustaka pemetaan menggunakan 11.0; konsumennya menggunakan preview. Dari direktori konsumen, jalankan dotnet run dengan F# 11 SDK pendukung.
Cakupan ekstensi di seluruh rakitan
Gunakan ekstensi publik dan buka modulnya sebelum menentukan fungsi sebaris dependen. Untuk batasan yang tidak terselesaikan yang diimpor, buka juga adaptor di konsumen; deklarasi produser open tidak ditransfer. API yang sudah diselesaikan tidak mengharuskan konsumen membuka kembali adaptornya.
Inferensi dan tanda tangan
Resolusi F# 11.2 menangguhkan beberapa pilihan yang dibuat mode F# 11.0 lebih awal. Pertimbangkan pembantu sebaris yang DateTime ditentukan dengan penerima:
let inline shift (date: System.DateTime) offset = date + offset
Dalam mode F# 11.0, formulir ini dapat menyimpulkan DateTime -> TimeSpan -> DateTime. Dengan resolusi ekstensi F# 11.2 diaktifkan, resolusi ekstensi dapat mempertahankan batasan untuk (DateTime or ^Offset) : (static member (+) : DateTime * ^Offset -> ^Result). Konsumen kemudian dapat memilih ekstensi publik dalam cakupan untuk jenis offset yang berbeda.
Konsumen tidak dapat memulihkan kegeneralan yang hilang di pustaka F# 11.0. Periksa tanda tangan yang diekspor; produsen mungkin juga memerlukan inferensi F# 11.2 diaktifkan.
Batasan dan pemecahan masalah pratinjau
- Untuk FS0001 atau FS0193, periksa pengaturan bahasa, visibilitas publik, dan cakupan adaptor.
- Jika file produsen yang lebih baru menyelesaikan contoh jalur untuk
Complex, pembuatan kode dapat gagal dengan FS0971,Undefined value 'get_Coordinates'. Pertahankan resolusi tersebut dengan definisinya atau di konsumen terpisah. - Adaptor yang bersaing dapat membuat pemilihan anggota ambigu.