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.
Selamat datang di F# 11! Rilis ini membantu Anda menjaga model domain tetap berbeda, menggunakan kembali algoritma generik, mengoordinasikan pekerjaan asinkron, dan mengurangi penggunaan memori kompilator.
- Rekaman menyebarkan bidang penggunaan kembali tanpa memaksa model penyimpanan dan aplikasi untuk berbagi jenis.
- Anggota ekstensi dalam algoritma generik memungkinkan jenis operasi berbagi yang ada tanpa pembungkus.
- integrasi .NET mempertahankan metadata delegasi dan memanfaatkan kembali dokumentasi API.
- Helper async dan task meneruskan pembatalan lintas batas API dan membatasi pekerjaan bersamaan.
- Peningkatan pada kompiler dan pustaka mengurangi analisis berulang dan menghindari sebagian alokasi.
Terima kasih kepada komunitas F# atas proposal, implementasi, ulasan, dan pengujian di balik perubahan ini. Tautan kontributor menyertai fitur di bawah ini.
Note
Contohnya memerlukan kompilator F# 11 dan FSharp.Core 11 dengan fitur yang ditampilkan. Dalam SDK .NET 11.0.100, fitur F# 11.2 memerlukan <LangVersion>preview</LangVersion>; lihat pengaturan bahasa dan dukungan SDK yang direncanakan. Contoh runtime-async memiliki persyaratan runtime tambahan; SDK .NET 11 sebelumnya saja mungkin tidak berisi setiap perubahan.
Sebaran data: model yang berbeda tanpa pemetaan berulang
Untuk menjaga model penyimpanan dan aplikasi tetap berbeda, spread record menggunakan kembali field bersama mereka tanpa deklarasi berulang. Di sini model berbagi detail pelanggan tetapi menggunakan jenis pengidentifikasi yang berbeda:
open System
type CustomerId = CustomerId of Guid
type CustomerDetails = { Name: string; PreferredLanguage: string }
type CustomerRow =
{ ...CustomerDetails
Id: string
InternalNote: string }
type Customer =
{ ...CustomerDetails
Id: CustomerId }
...CustomerDetails menambahkan deklarasi field. Di batas, ...row menyalin nilai saat konversi mengurai pengidentifikasi:
let toCustomer (row: CustomerRow) =
match Guid.TryParse row.Id with
| true, id -> Ok ({ ...row; Id = CustomerId id }: Customer)
| false, _ -> Error "Invalid customer identifier."
Tujuan menyimpan detail bersama, menggantikan Id, dan mengecualikan InternalNote.
Guid.TryParse, bukan spread, memvalidasi pengidentifikasi; input cacat menghasilkan Error.
Ini mengatasi masalah pemetaan lapisan aplikasi. Nilai spread juga menggabungkan record bernama dan anonim; jenis spread hanya membuat record bernama. Field yang muncul belakangan akan diprioritaskan, dan sumber berjalan sekali secara berurutan. Lihat operator spread record untuk contoh komposisi dan risiko evolusi bersama.
Terima kasih kepada @brianrourkeboll atas spread rekor.
Anggota ekstensi untuk algoritma generik, operator, dan tuple
Untuk berbagi algoritma di seluruh tipe yang sudah ada, F# 11.2 memungkinkan anggota perluasan memenuhi batasan parameter tipe yang diselesaikan secara statis (SRTP). Misalnya, satu transformasi dapat menangani daftar dan opsi tanpa tipe pembungkus.
Note
Dalam SDK .NET 11.0.100, aktifkan <LangVersion>preview</LangVersion> di tempat ekstensi di-resolve dan buka modulnya. Lihat persyaratan library.
Terapkan transformasi di seluruh daftar dan opsi
Dengan pustaka map dalam cakupan, aplikasi dapat membersihkan salah satu dari kedua representasi, mengubah jenis elemen, dan menyusun transformasi:
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]
Adaptor dalam Structural.Adapters menggunakan operasi yang sudah dikenal, daripada memaksakan kontainer baru:
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
Fungsi bersama dalam Structural.Algorithms meminta jenis input untuk anggota statis Map :
let inline map (mapping: 'T -> 'U) (source: ^Input) : ^Output =
(^Input :
(static member Map: ^Input * ('T -> 'U) -> ^Output)
(source, mapping))
^Input dan ^Output merupakan tipe yang diselesaikan secara statis. Pada setiap panggilan konkret, pengkompilasi memilih anggota dengan signatur yang diperlukan. Ekstensi tidak menambahkan implementasi antarmuka.
Aplikasi lengkap membuka kedua modul.
Operator, konteks, dan jenis output
Mekanisme yang sama mendukung operator tuple dan penyebaran konteks dan konstruksi yang diarahkan output. header tuple native memerlukan fitur F# 11.2 diaktifkan pada deklarasinya serta di lokasi pemanggilan penyelesaian ekstensi.
Tambahan terpisah , AllowOverloadOnReturnType, memungkinkan satu pabrik mendeklarasikan kelebihan beban yang dibedakan oleh pengembaliannya:
type Inject =
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T list = [value]
[<AllowOverloadOnReturnType>]
static member Pure(value: 'T) : 'T option = Some value
let batch: string list = Inject.Pure "ready"
let optional: string option = Inject.Pure "ready"
// ["ready"] and Some "ready"
Atribut Core 11 ini tidak memerlukan pratinjau. Anotasi hasil memilih kelebihan beban; tanpa itu panggilan tetap ambigu. Lihat Pilih berdasarkan tipe pengembalian.
Terima kasih kepada @gusty karena telah memperluas dukungan pemrograman generik.
integrasi .NET dan pengalaman pengembangan
Delegasi langsung mempertahankan kontrak asli handler
Untuk mengekspos nama handler dan atribut pengikatan ke kerangka kerja, F# 11 dapat menargetkan fungsi bernama yang kompatibel secara langsung alih-alih pembungkus. ASP.NET Core API Minimal kemudian dapat membaca rute, layanan, dan kontrak header:
open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.DependencyInjection
type ShipmentStore() =
member _.Status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
let status
(trackingId: string)
([<FromServices>] store: ShipmentStore)
([<FromHeader(Name = "X-Customer")>] customer: string) =
$"{customer}: {trackingId} is {store.Status trackingId}"
let handler = Func<string, ShipmentStore, string, string>(status)
Lihat proyek yang dapat dijalankan dan aturan konstruksi/kompatibilitas. Pengambilan dan adaptasi argumen masih memerlukan pembungkus.
Terima kasih kepada @kerams untuk delegasi langsung.
Menggunakan kembali dokumentasi XML
Untuk mempertahankan komentar API bersama sekali, tempatkan di shared.xml:
<docs>
<tracking>
<remarks>An unrecognized tracking identifier has status Unknown.</remarks>
</tracking>
</docs>
Dengan GenerateDocumentationFile diaktifkan, F# 11 meluas <include> ke entri anggota yang dipancarkan:
/// <summary>Find the latest status of a shipment.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let status trackingId =
if trackingId = "SHP-1042" then "Dispatched" else "Unknown"
/// <summary>Find the latest statuses of several shipments.</summary>
/// <include file="shared.xml" path="/docs/tracking/*"/>
let statuses trackingIds =
trackingIds |> List.map (fun id -> id, status id)
Termasuk implementasi memperbarui kedua deskripsi dari satu fragmen. Secara terpisah, pemecah masalah alat mengekspos deskripsi dasar untuk <inheritdoc/>; pengkompilasi meninggalkan tag tersebut untuk konsumen dokumentasi. Lihat dokumentasi XML untuk contoh dan aturan resolusi.
Berkat @T-Gro untuk dokumentasi XML bersama di FSharp.Core.
Men-debug fungsi sebaris dalam build yang tidak dioptimalkan
Untuk memeriksa argumen pembantu sebaris, atur titik henti di dalam build yang tidak dioptimalkan let total dan masuk ke addFee. F# 11 sekarang lebih suka metode yang dapat dipanggil dalam build ini:
let inline addFee (fee: decimal) (amount: decimal) =
let total = amount + fee
total
let invoiceTotal = addFee 2m 40m
printfn "%M" invoiceTotal
// 42
Build yang dioptimalkan mempertahankan inlining. Beberapa konstruksi masih harus diperluas. Lihat Men-debug fungsi sebaris untuk batas dan kontrol tersebut --always-inline .
Async dan koordinasi tugas
Untuk menyambungkan alur kerja F# dengan API berbasis tugas, FSharp.Core 11 menambahkan bridge yang mendukung pembatalan, batch terbatas, dan transformasi hasil.
Terikat I/O yang luar biasa, bukan hanya koleksi hasil
Batch pembacaan yang besar dapat memulai lebih banyak tugas daripada jumlah tugas yang seharusnya masih tertunda pada suatu aplikasi. Untuk memutuskan kapan pekerjaan berbasis tugas dimulai, koordinator membutuhkan pabrik, bukan tugas yang telah dimulai:
open System.IO
open System.Threading
let readFiles (paths: seq<string>) (ct: CancellationToken) =
paths
|> Seq.map (fun path ->
fun token -> File.ReadAllTextAsync(path, token))
|> Task.parallelLimit 4 ct
Paling banyak empat bacaan berjalan secara bersamaan; hasil mempertahankan urutan input. Kegagalan meminta pembatalan terkait melalui token yang disediakan. Ini membatasi jumlah permintaan yang diproses secara bersamaan, bukan jumlah permintaan per detik. Lihat Fungsi asinkron dan tugas untuk varian berurutan dan hasil unit.
Pertahankan pembatalan dan pengecualian yang mendasarinya di seluruh batas
Saat alur kerja memulai pekerjaan eksternal, tokennya harus ikut terbawa bersama pekerjaan tersebut.
Async.StartTaskImmediate meneruskan token alur kerja yang sedang berjalan ke task factory dan menunggu penyelesaian:
open System.IO
let tryReadFile path =
async {
try
let! contents =
Async.StartTaskImmediate(fun ct -> File.ReadAllTextAsync(path, ct))
return Some contents
with :? FileNotFoundException ->
return None
}
Kegagalan satu tugas diteruskan ke handler ini sebagai penyebab dasarnya FileNotFoundException. Untuk tugas atau tugas nilai yang sudah dibuat, gunakan Async.Await; Async.AwaitTask yang lebih lama mempertahankan perilaku pembungkus pengecualiannya.
Untuk pemanggil berbasis tugas, Task.startAsyncImmediate token workflow menyediakan jembatan balik. Ini dimulai pada utas panggilan sampai suspensi asinkron pertama.
Gunakan fungsi pembantu yang sesuai untuk transformasi sederhana
Untuk mengubah satu hasil tanpa ekspresi komputasi penuh, gunakan pembantu yang cocok di Microsoft.FSharp.Control. Misalnya, baca sebuah file, dapatkan panjangnya, dan kembalikan kegagalan biasa sebagai Error:
open System.IO
open System.Threading
let fileLength (path: string) (ct: CancellationToken) =
File.ReadAllTextAsync(path, ct)
|> Task.map String.length
|> Task.catch
Sebuah file yang berisi Report 1 menghasilkan Ok 8; file yang hilang menghasilkan Error yang berisi FileNotFoundException. Pembatalan masih diteruskan.
Async dan ValueTask menawarkan pencocokan map, bind, dan pembantu kesalahan; lihat Fungsi asinkron dan tugas. Gunakan ekspresi komputasi untuk cabang atau beberapa nilai dependen.
Fungsi pembantu ValueTask memerlukan build Core netstandard2.1 atau net10.0 dan penggunaan sekali pakai.
Terima kasih kepada @bartelink atas kontribusi API asinkron/tugas.
Munculkan kembali setelah pelaporan kegagalan asinkron
Pencatatan operasional harus menambahkan informasi tanpa mengubah pengecualian yang ditangkap pemanggil atau menyembunyikan tempat kegagalan bermula. Handler ekspresi komputasi kini dapat menggunakan reraise () bahkan setelah mereka menunggu pencatat:
Note
Rethrow pada computation expression adalah fitur F# 11.2. Di SDK .NET 11.0.100, aktifkan <LangVersion>preview</LangVersion>.
open System
open System.IO
open System.Threading.Tasks
let readReport (logFailure: exn -> Task) path =
task {
try
return! File.ReadAllTextAsync(path)
with :? IOException as error ->
do! logFailure error
return reraise ()
}
Setelah pencatatan log berhasil, pengecualian yang sama dan informasi lokasi asli pelemparan disebarluaskan. Kegagalan logger dapat menggantikan yang asli. Ini menangani permintaan observability async tanpa async runtime. Lihat Rethrow dari ekspresi komputasi.
Terima kasih untuk @xperiandri atas dukungan ini.
FSharp.Core dapat dibangun berdasarkan .NET modern
Untuk menggunakan API modern tanpa menghilangkan target yang lebih lama, FSharp.Core menambahkan net10.0 bersama netstandard2.0 dan netstandard2.1. NuGet memilih build yang kompatibel. Pengemasan tool yang diperbarui juga memungkinkan pelepasan sumber daya asinkron berbasis tugas di F# Interactive, seperti dalam proyek yang dikompilasi. Lihat kerangka kerja yang ditargetkan FSharp.Core.
Terima kasih untuk @T-Gropenargetan Core modern dan kemasan alat SDK.
Asinkron runtime untuk penulis builder
Penulis penyusun dapat membiarkan runtime menangguhkan dan melanjutkan metode yang ditandai alih-alih menggunakan mesin status yang dihasilkan kompiler. Builder eksperimental yang didefinisikan pustaka mengekspos mekanisme sebagai ekspresi komputasi:
let readManifest (path: string) =
runtimeTask {
let! text = System.IO.File.ReadAllTextAsync path
return text.Length
}
Note
Penyusun eksperimental ini menggunakan fitur F# 11.2 di mana implementasi sebarisnya ditentukan atau diperluas; di .NET 11.0.100 SDK, aktifkan pratinjau. Ini juga memerlukan build dan referensi milik Core 11 net10.0 dan runtime dengan dukungan runtime-async. Kombinasi runtime/referensi terverifikasi di sini adalah .NET 11 RC1. Menargetkan .NET 10 saja tidak cukup. Lihat Eksperimen asinkron runtime.
FSharp.Core tidak menyertakan sampel ini sebagai builder tujuan umum. Kode task dan async biasa tidak secara otomatis beralih ke mode tersebut, dan pemanggil API biasa yang sudah dikompilasi yang mengembalikan Task pada dasarnya juga tidak memerlukan fitur pratinjau.
Terima kasih kepada @majocha untuk runtime asinkron.
Teks ramah AOT asli dan array dua dimensi
Untuk mendukung Native AOT, F# 11 menghapus pemformatan berbasis refleksi dan persyaratan alokasi dinamis dari operasi teks dan array yang lebih umum.
Mencetak nilai dan membangun string tanpa lapisan format redundan
Jika Anda sudah memiliki string, Anda tidak memerlukan format "%s" hanya untuk menuliskannya. Fungsi Inti baru print dan printn menerima nilai melalui konversi string milik F#; printn menambahkan baris baru:
let report (processed: int) (total: decimal) (elapsed: TimeSpan) : string =
$"{processed} items, total {total:F2}, elapsed {elapsed.TotalMilliseconds:F0} ms"
printn (report 12 145.50m (TimeSpan.FromMilliseconds 250.0))
// 12 items, total 145.50, elapsed 250 ms
Interpolasi string menggunakan konversi langsung dan String.Concat dalam lebih banyak kasus. .NET-format lubang seperti {total:F2} menggunakan pemformatan invarian dan kotak nilai; %A dan formulir cetak bantalan presisi masih memerlukan pemformatan cetak. Pilih kultur eksplisit untuk output yang dilokalkan.
Dengan --reflectionfree, record dan union juga memiliki output ToString yang dihasilkan. Lihat Interpolasi dan Native AOT untuk contoh kultur dan jalur yang didukung.
Berkat @bbatsov untuk fungsi cetak dan @charlesroddie untuk implementasi interpolasi.
Pertahankan operasi kisi berbasis nol biasa
Grid berbasis nol seharusnya tidak perlu modul pengganti Array2D hanya untuk menghindari jalur alokasi untuk kode dinamis. Implementasi Core yang diperbarui mendukung inisialisasi dan transformasi yang familier:
let grid = Array2D.init 3 4 (fun row column -> row * 4 + column)
let doubled = grid |> Array2D.map (fun value -> value * 2)
let snapshot = Array2D.copy doubled
printn $"{snapshot.GetLength(0)} x {snapshot.GetLength(1)}; last = {snapshot[2, 3]}"
// 3 x 4; last = 22
Array2D.create, init, dan rebase gunakan jalur alokasi berbasis nol pada semua build Core. Bounds-preserving map, mapi, dan copy dapat menggunakan API alokasi .NET yang lebih baru dalam net10.0 build; build .NET Standard mempertahankan fallback mereka. Array berbasis non-nol tidak usang, tetapi pembatasan alokasi aslinya tetap ada.
Lihat Array dua dimensi dan AOT Asli untuk jalur alokasi dan batasan yang tersisa. Periksa diagnostik penerbitan seluruh aplikasi.
Terima kasih kepada @T-Gro atas peningkatan Array2D.
Lebih sedikit memori yang digunakan dan lebih sedikit pekerjaan yang berulang
F# 11 mengurangi analisis kompilator berulang dan beberapa alokasi memori pada aplikasi. Ini memengaruhi beban kerja yang berbeda.
Berbagi pustaka referensi antarproyek
Untuk menghindari salinan berulang dependensi biner bersama, layanan bahasa dapat berbagi grafik pustaka yang diimpor yang memenuhi syarat di seluruh proyek.
Dalam eksperimen retained memory milik PR implementasi, solusi ReSharper.FSharp sepuluh proyek turun dari 1.547,2 MB menjadi 1.002,2 MB dengan berbagi diaktifkan. Itu adalah pengukuran aktif/nonaktif untuk beban kerja tersebut, bukan pengurangan umum F# 10-ke-11. Solusi yang didominasi oleh referensi proyek daripada referensi biner bersama dapat mengalami peningkatan yang jauh lebih kecil.
Pembacaan metadata dan spesialisasi sebaris juga mengurangi alokasi sementara, yang merupakan metrik terpisah. Lihat Kinerja perkakas untuk cakupan dan kontrol.
Terima kasih kepada @auduchinok atas impor pustaka bersama.
Kompilasi tugas independen secara paralel secara bawaan
Kompilasi paralel sekarang diaktifkan secara default, dengan output deterministik dan aturan urutan sumber dipertahankan. Cache overload dan peningkatan pola berpagar mencegah pekerjaan berulang lainnya. Visual Studio/FCS juga mengurangi analisis terhadap dokumen yang tidak berubah dan membatasi pemeriksaan serentak; lihat Performa alat.
Hindari overhead alokasi callback dan hasil kosong
Fungsi List dan Array yang dipilih kini menyisipkan callback secara inline. Untuk helper inline Anda sendiri, OptimizeClosureIfNotInlined menyesuaikan callback curried opak satu kali:
let inline foldItems
([<InlineIfLambda; OptimizeClosureIfNotInlined>] folder)
state
(items: 'T[]) =
let mutable total = state
for item in items do
total <- folder total item
total
Alokasi lain dapat tetap ada. Lihat Mengoptimalkan callback yang di-curry untuk persyaratan.
Hasil kosong adalah kasus umum lain dari proses yang berhasil. Beban kerja FSharpLint yang memotivasi berulang kali menggabungkan aturan yang tidak menemukan tidak ada peringatan. Core sekarang menggunakan kembali array kosong bersama dalam lebih banyak operasi:
let checkTitle (title: string) =
if String.IsNullOrWhiteSpace title then [|"A title is required."|]
else Array.empty
let warnings = ["Introduction"; "Installation"] |> List.toArray |> Array.collect checkTitle
let sharedEmpty = obj.ReferenceEquals(warnings, Array.empty<string>)
// true: no new empty result array
Perubahan menghapus objek larik kosong yang berlebihan, bukan setiap alokasi dalam alur. Jangan mengandalkan hasil kosong terpisah yang memiliki identitas referensi yang unik.
Berkat @T-Grocallback koleksi inline dan array kosong bersama.
Pemanggilan API yang lebih tepat dan kenyamanan sehari-hari
Harus menggunakan nama saat urutan parameter yang dihasilkan dapat berubah
Untuk melindungi API yang dihasilkan dari perubahan urutan parameter senyap, penulis dapat memerlukan argumen bernama:
Note
Enforcement adalah fitur F# 11.2; di SDK .NET 11.0.100, aktifkan pratinjau. Definisi yang tersedia System.Diagnostics.CodeAnalysis.RequireNamedArgumentsAttribute juga diperlukan.
Contoh lengkap memberikan definisi kompatibilitas untuk referensi yang tidak memiliki definisi tersebut.
open System.Diagnostics.CodeAnalysis
type Query =
[<RequireNamedArguments>]
static member Execute(numerator: decimal, denominator: decimal) =
numerator / denominator
let ratio = Query.Execute(numerator = 3m, denominator = 4m)
// 0.75M; Query.Execute(3m, 4m) is rejected.
Menyusun ulang deklarasi mempertahankan hasil panggilan bernama, 0.75M; panggilan posisional ditolak. Untuk kasus penggunaan API yang dihasilkan, penyedia harus memancarkan anotasi.
Mengenali argumen generik yang sudah dibungkus
Untuk menghindari pembungkusan tugas yang ada sebagai nilai hasil biasa, aturan overload pilihan terakhir yang baru mengutamakan Task<'T> daripada 'T tanpa pembungkus:
Note
Perilaku pemecah tiebreaker dan prioritas overload yang paling konkret adalah fitur F# 11.2; di .NET 11.0.100 SDK, aktifkan pratinjau.
open System.Threading.Tasks
let pending = Task.FromResult 42
let wrapped = ValueTask<_>(pending)
let answer: int = wrapped.Result
// ValueTask<int>, not ValueTask<Task<int>>
Hasilnya adalah ValueTask<int>, bukan ValueTask<Task<int>>. Lihat Pemilihan kelebihan beban untuk melihat batasan aturan.
Lebih memilih overload baru tanpa menghapus entry point lama
Library mungkin menyimpan metode diagnostik lama untuk biner yang ada sambil menambahkan overload yang menyertakan lokasi sumber pemanggil.
OverloadResolutionPriorityAttribute membuat preferensi tersebut eksplisit untuk pemanggil yang baru dikompilasi:
open System.Runtime.CompilerServices
type Check =
static member That(condition: bool) =
if condition then "OK" else "Check failed."
[<OverloadResolutionPriority(1)>]
static member That(condition: bool, [<CallerLineNumber>] ?line: int) =
if condition then "OK" else $"Check failed at line {defaultArg line 0}."
let stock = 0
let diagnostic = Check.That(stock > 0)
// The diagnostic includes the caller's source line.
Panggilan yang tidak berubah memilih overload yang peka terhadap baris. Lihat Pemilihan overload untuk cakupan dan aturan kompatibilitas.
Gunakan jaminan nullability bersyarat .NET
Jika jalur file Anda non-null, hasil Path API yang dapat bernilai null tidak boleh memaksa pemeriksaan yang dikesampingkan oleh kontraknya sendiri. F# 11 menggunakan janji .NET NotNullIfNotNull yang ada: input bernama non-null menyiratkan hasil non-null.
open System.IO
let fileName: string = Path.GetFileName("reports/annual.csv")
let extension: string = Path.GetExtension(fileName)
printn (extension.ToUpperInvariant())
// .CSV
let fileNameOf (path: string | null) : string | null =
Path.GetFileName path
Aktifkan <Nullable>enable</Nullable>. Input nullable masih memerlukan penanganan nullable; lihat Kontrak pengembalian kondisional. Untuk API C# T? yang tidak dibatasi, gunakan Unchecked.withNull helper terpisah.
Terima kasih kepada @kerams untuk dukungan nullability bersyarat.
Perbaikan bahasa minor
Untuk memanggil tipe record dengan sintaks konstruktor .NET, gunakan F# 11.2 konstruktor record:
type Person = { Name: string; Age: int }
let person = Person(Name = "Ada", Age = 36)
Argumen bernama menghindari ketergantungan pada urutan bidang. Terima kasih kepada @charlesroddie atas fitur ini.
Untuk sumber kondisional, #elif menghindari grup #if bertingkat:
#if EU_WAREHOUSE
let dispatchTimeZone = "Europe/Paris"
#elif US_WAREHOUSE
let dispatchTimeZone = "America/New_York"
#else
let dispatchTimeZone = "UTC"
#endif
System.Console.WriteLine(dispatchTimeZone)
Cabang pertama yang cocok menang. Proyek sampel mendefinisikan EU_WAREHOUSE dan mencetak Europe/Paris; tanpa kedua simbol didefinisikan, outputnya adalah UTC.
Untuk kasus alat dan interop terkait:
- Untuk mengidentifikasi toolchain interaktif Anda, gunakan
#version. - Untuk berbagi sumber dengan host eksternal, F# menerima
#:baris direktif host tanpa menafsirkannya sebagai paket atau instruksi build. - Untuk menghindari deklarasi antarmuka berlebihan, gunakan implementasi default antarmuka turunan.
- Untuk membaca state yang diwariskan di dalam closure biasa, gunakan dukungan baru untuk field dasar protected.
Sebelum pembaruan
Ingatlah perubahan konsekuensial ini saat meningkatkan:
| Kode yang terpengaruh | Action |
|---|---|
Pustaka dengan berkas .fsi |
Salin atribut kontrak yang diwajibkan ke dalam signature; ketiadaannya kini memicu error FS3888. |
| Library inline yang didukung oleh ekstensi | Tinjau tanda tangan yang disimpulkan dan persyaratan produsen/konsumen. Inferensi F# 11.2 dapat mempertahankan batasan yang lebih umum. |
| Delegasi metode instans | Validasikan receiver sebelum konstruksi langsung, ketika kegagalan receiver null sekarang terjadi. |
| Output interpolasi yang dilokalkan | Placeholder format .NET menggunakan kultur invarian. Pilih kultur eksplisit jika output terlokalkan diperlukan. |
Builder kustom dengan final do! |
Menyediakan pengikatan dan penyelesaian unit; final do! tidak lagi memilih anggota final-forwarding. |
Belum selesai let! |
Tambahkan badan komputasi berikut; pengikatan yang tidak lengkap sekarang memiliki pesan diagnostik yang lebih langsung. |
| Pengaturan bahasa lama | Mode bernomor yang didukung dimulai pada F# 8; mode indentasi non-ketat dihapus. Lihat Versi bahasa dan indentasi. |
| Operasi bitwise pada enum | Gunakan enum dengan tipe dasar integral; tipe dasar yang tidak valid akan ditolak. |