Ekspresi Komputasi
Ekspresi komputasi dalam F# menyediakan sintaks yang nyaman untuk menulis perhitungan yang dapat diurutkan dan digabungkan menggunakan konstruksi dan pengikatan alur kontrol. Tergantung jenis ekspresi komputasi, mereka dapat dianggap sebagai cara untuk mengekspresikan monad, monoid, transformator monad, dan funktor aplikatif. Namun, tidak seperti bahasa lain (seperti do-notation di Haskell), mereka tidak terikat dengan abstraksi tunggal, dan tidak mengandalkan makro atau bentuk metaprogram lainnya untuk mencapai sintaks yang nyaman dan sensitif konteks.
Gambaran Umum
Komputasi dapat mengambil banyak formulir. Bentuk komputasi yang paling umum adalah eksekusi utas tunggal, yang mudah dipahami dan dimodifikasi. Namun, tidak semua bentuk komputasi sesederhana eksekusi utas tunggal. Beberapa contohnya termasuk:
- Komputasi nondeterministik
- Komputasi asinkron
- Komputasi yang berpengaruh
- Komputasi generatif
Secara umum, ada komputasi sensitif konteks yang harus Anda lakukan di bagian tertentu dari aplikasi. Menulis kode sensitif konteks bisa menantang, karena mudah untuk "membocorkan" komputasi di luar konteks tertentu tanpa abstraksi untuk mencegah Anda melakukannya. Abstraksi ini sering menantang untuk ditulis sendiri, itulah sebabnya F# memiliki cara umum untuk melakukannya yang disebut ekspresi komputasi.
Ekspresi komputasi menawarkan sintaksis seragam dan model abstraksi untuk pengodean komputasi sensitif konteks.
Setiap ekspresi komputasi didukung oleh jenis penyusun. Jenis penyusun menentukan operasi yang tersedia untuk ekspresi komputasi. Lihat Membuat Jenis Baru Ekspresi Komputasi, yang memperlihatkan cara membuat ekspresi komputasi kustom.
Gambaran umum sintaksis
Semua ekspresi komputasi memiliki formulir berikut:
builder-expr { cexper }
Dalam formulir ini, builder-expr
adalah nama jenis penyusun yang menentukan ekspresi komputasi, dan cexper
merupakan isi ekspresi pada ekspresi komputasi. Misalnya, kode ekspresi komputasi async
dapat terlihat seperti ini:
let fetchAndDownload url =
async {
let! data = downloadData url
let processedData = processData data
return processedData
}
Ada sintaks tambahan khusus yang tersedia dalam ekspresi komputasi, seperti yang ditunjukkan pada contoh sebelumnya. Formulir ekspresi berikut dimungkinkan dengan ekspresi komputasi:
expr { let! ... }
expr { and! ... }
expr { do! ... }
expr { yield ... }
expr { yield! ... }
expr { return ... }
expr { return! ... }
expr { match! ... }
Masing-masing kata kunci ini, dan kata kunci F# standar lainnya hanya tersedia dalam ekspresi komputasi jika telah ditentukan dalam jenis penyusun dukungan. Satu-satunya pengecualian untuk ini adalah match!
, yang merupakan gula sintaksis untuk penggunaan let!
diikuti oleh kecocokan pola pada hasilnya.
Jenis penyusun adalah objek yang mendefinisikan metode khusus yang mengatur cara fragmen ekspresi komputasi digabungkan; artinya, metodenya mengontrol bagaimana ekspresi komputasi berperilaku. Cara lain untuk menggambarkan kelas penyusun adalah dengan mengatakan bahwa itu memungkinkan Anda untuk menyesuaikan pengoperasian banyak konstruksi F#, seperti perulangan dan pengikatan.
let!
Kata kunci let!
mengikat hasil panggilan ke ekspresi komputasi lain ke nama:
let doThingsAsync url =
async {
let! data = getDataAsync url
...
}
Jika Anda mengikat panggilan ke ekspresi komputasi dengan let
, Anda tidak akan mendapatkan hasil ekspresi komputasi. Sebagai gantinya, Anda akan mengikat nilai panggilan belum direalisasikan ke ekspresi komputasi tersebut. Gunakan let!
untuk mengikat hasilnya.
let!
ditentukan oleh anggota Bind(x, f)
pada jenis penyusun.
and!
Kata and!
kunci memungkinkan Anda untuk mengikat hasil beberapa panggilan ekspresi komputasi dengan cara yang berkinerja.
let doThingsAsync url =
async {
let! data = getDataAsync url
and! moreData = getMoreDataAsync anotherUrl
and! evenMoreData = getEvenMoreDataAsync someUrl
...
}
Menggunakan serangkaian let! ... let! ...
kekuatan eksekusi ulang pengikatan mahal, jadi menggunakan let! ... and! ...
harus digunakan saat mengikat hasil banyak ekspresi komputasi.
and!
didefinisikan terutama oleh MergeSources(x1, x2)
anggota pada jenis penyusun.
Secara opsional, MergeSourcesN(x1, x2 ..., xN)
dapat didefinisikan untuk mengurangi jumlah simpul yang menggeser, dan BindN(x1, x2 ..., xN, f)
, atau BindNReturn(x1, x2, ..., xN, f)
dapat didefinisikan untuk mengikat hasil ekspresi komputasi secara efisien tanpa memutar simpul.
do!
Kata kunci do!
adalah untuk memanggil ekspresi komputasi yang mengembalikan tipe seperti unit
(ditentukan oleh anggota Zero
pada pembuat):
let doThingsAsync data url =
async {
do! submitData data url
...
}
Untuk alur kerja asinkron, jenis ini adalah Async<unit>
. Untuk ekspresi komputasi lainnya, jenisnya kemungkinan adalah CExpType<unit>
.
do!
didefinisikan oleh anggota Bind(x, f)
pada jenis penyusun, di mana f
menghasilkan unit
.
yield
Kata kunci yield
adalah untuk mengembalikan nilai dari ekspresi komputasi sehingga dapat dikonsumsi sebagai IEnumerable<T>:
let squares =
seq {
for i in 1..10 do
yield i * i
}
for sq in squares do
printfn $"%d{sq}"
Dalam kebanyakan kasus, itu dapat dihilangkan oleh pemanggil. Cara paling umum untuk menghilangkan yield
adalah dengan operator ->
:
let squares =
seq {
for i in 1..10 -> i * i
}
for sq in squares do
printfn $"%d{sq}"
Untuk ekspresi yang lebih kompleks yang mungkin menghasilkan banyak nilai yang berbeda, dan mungkin secara kondisional, hanya menghilangkan kata kunci dapat melakukan:
let weekdays includeWeekend =
seq {
"Monday"
"Tuesday"
"Wednesday"
"Thursday"
"Friday"
if includeWeekend then
"Saturday"
"Sunday"
}
Seperti halnya kata kunci hasil dalam C#, setiap elemen dalam ekspresi komputasi dihasilkan kembali saat pengulangan.
yield
didefinisikan oleh anggota Yield(x)
pada jenis penyusun, di mana x
adalah item yang akan dihasilkan kembali.
yield!
Kata kunci yield!
adalah untuk meratakan kumpulan nilai dari ekspresi komputasi:
let squares =
seq {
for i in 1..3 -> i * i
}
let cubes =
seq {
for i in 1..3 -> i * i * i
}
let squaresAndCubes =
seq {
yield! squares
yield! cubes
}
printfn $"{squaresAndCubes}" // Prints - 1; 4; 9; 1; 8; 27
Saat dievaluasi, ekspresi komputasi yang dipanggil oleh yield!
akan membuat itemnya dikembalikan satu per satu, meratakan hasilnya.
yield!
ditentukan oleh anggota YieldFrom(x)
pada jenis pembuat, di mana x
adalah kumpulan nilai.
Tidak seperti yield
, yield!
harus ditentukan secara eksplisit. Perilakunya tidak tersirat dalam ekspresi komputasi.
return
Kata kunci return
membungkus nilai dalam jenis yang sesuai dengan ekspresi komputasi. Selain ekspresi komputasi menggunakan yield
, ekspresi komputasi digunakan untuk "menyelesaikan" ekspresi komputasi:
let req = // 'req' is of type 'Async<data>'
async {
let! data = fetch url
return data
}
// 'result' is of type 'data'
let result = Async.RunSynchronously req
return
ditentukan oleh anggota Return(x)
pada jenis penyusun, di mana x
adalah item untuk dibungkus. Untuk let! ... return
penggunaan, BindReturn(x, f)
dapat digunakan untuk meningkatkan performa.
return!
Kata kunci return!
menyadari nilai ekspresi komputasi dan bungkus yang menghasilkan jenis yang sesuai dengan ekspresi komputasi:
let req = // 'req' is of type 'Async<data>'
async {
return! fetch url
}
// 'result' is of type 'data'
let result = Async.RunSynchronously req
return!
ditentukan oleh anggota ReturnFrom(x)
pada jenis penyusun, di mana x
adalah ekspresi komputasi lain.
match!
Kata kunci match!
memungkinkan Anda untuk menyejajarkan panggilan ke ekspresi komputasi lain dan pencocokan pola pada hasilnya:
let doThingsAsync url =
async {
match! callService url with
| Some data -> ...
| None -> ...
}
Saat memanggil ekspresi komputasi dengan match!
, itu akan mewujudkan hasil panggilan seperti let!
. Ini sering digunakan saat memanggil ekspresi komputasi di mana hasilnya bersifat opsional.
Ekspresi komputasi bawaan
Pustaka inti F# mendefinisikan empat ekspresi komputasi bawaan: Ekspresi Urutan, Ekspresi Asinkron, Ekspresi tugas, dan Ekspresi Kueri.
Membuat Tipe Ekspresi Komputasi Baru
Anda dapat menentukan karakteristik ekspresi komputasi Anda sendiri dengan membuat kelas penyusun dan menentukan metode khusus tertentu pada kelas . Kelas penyusun dapat secara opsional menentukan metode seperti yang tercantum dalam tabel berikut.
Tabel berikut ini menjelaskan metode yang dapat digunakan di kelas penyusun alur kerja.
Metode | Tanda tangan umum | Keterangan |
---|---|---|
Bind |
M<'T> * ('T -> M<'U>) -> M<'U> |
Dipanggil untuk let! dan do! dalam ekspresi komputasi. |
BindN |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Dipanggil untuk ekspresi komputasi yang efisien let! dan and! dalam penggabungan input.misalnya Bind3 , Bind4 . |
Delay |
(unit -> M<'T>) -> Delayed<'T> |
Membungkus ekspresi komputasi sebagai fungsi. Delayed<'T> dapat berupa jenis apa pun, umumnya M<'T> atau unit -> M<'T> digunakan. Implementasi default mengembalikan M<'T> . |
Return |
'T -> M<'T> |
Dipanggil untuk return dalam ekspresi komputasi. |
ReturnFrom |
M<'T> -> M<'T> |
Dipanggil untuk return! dalam ekspresi komputasi. |
BindReturn |
(M<'T1> * ('T1 -> 'T2)) -> M<'T2> |
Dipanggil untuk efisien let! ... return dalam ekspresi komputasi. |
BindNReturn |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Dipanggil untuk efisien let! ... and! ... return dalam ekspresi komputasi tanpa menggabungkan input.misalnya Bind3Return , Bind4Return . |
MergeSources |
(M<'T1> * M<'T2>) -> M<'T1 * 'T2> |
Dipanggil untuk and! dalam ekspresi komputasi. |
MergeSourcesN |
(M<'T1> * M<'T2> * ... * M<'TN>) -> M<'T1 * 'T2 * ... * 'TN> |
Dipanggil untuk and! dalam ekspresi komputasi, tetapi meningkatkan efisiensi dengan mengurangi jumlah simpul yang menggeser.misalnya MergeSources3 , MergeSources4 . |
Run |
Delayed<'T> -> M<'T> atauM<'T> -> 'T |
Menjalankan ekspresi komputasi. |
Combine |
M<'T> * Delayed<'T> -> M<'T> atauM<unit> * M<'T> -> M<'T> |
Dipanggil untuk pengurutan dalam ekspresi komputasi. |
For |
seq<'T> * ('T -> M<'U>) -> M<'U> atauseq<'T> * ('T -> M<'U>) -> seq<M<'U>> |
Dipanggil untuk ekspresi for...do dalam ekspresi komputasi. |
TryFinally |
Delayed<'T> * (unit -> unit) -> M<'T> |
Dipanggil untuk ekspresi try...finally dalam ekspresi komputasi. |
TryWith |
Delayed<'T> * (exn -> M<'T>) -> M<'T> |
Dipanggil untuk ekspresi try...with dalam ekspresi komputasi. |
Using |
'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable |
Dipanggil untuk pengikatan use dalam ekspresi komputasi. |
While |
(unit -> bool) * Delayed<'T> -> M<'T> Atau(unit -> bool) * Delayed<unit> -> M<unit> |
Dipanggil untuk ekspresi while...do dalam ekspresi komputasi. |
Yield |
'T -> M<'T> |
Dipanggil untuk ekspresi yield dalam ekspresi komputasi. |
YieldFrom |
M<'T> -> M<'T> |
Dipanggil untuk ekspresi yield! dalam ekspresi komputasi. |
Zero |
unit -> M<'T> |
Dipanggil untuk cabang if...then ekspresi else kosong dalam ekspresi perhitungan. |
Quote |
Quotations.Expr<'T> -> Quotations.Expr<'T> |
Menunjukkan bahwa ekspresi komputasi diteruskan ke anggota Run sebagai kutipan. Ini menerjemahkan semua instans komputasi ke dalam kutipan. |
Banyak metode di kelas penyusun menggunakan dan mengembalikan konstruksi M<'T>
, yang biasanya merupakan jenis yang ditentukan secara terpisah yang mencirikan jenis komputasi yang digabungkan, misalnya, Async<'T>
untuk ekspresi asinkron dan Seq<'T>
untuk alur kerja urutan. Tanda tangan dari metode ini memungkinkan mereka untuk digabungkan dan disarangkan satu sama lain, sehingga objek alur kerja yang dikembalikan dari satu konstruksi dapat diteruskan ke yang berikutnya.
Banyak fungsi menggunakan hasil Delay
sebagai argumen: Run
, While
, TryWith
, TryFinally
, dan Combine
. Jenis Delayed<'T>
adalah jenis pengembalian Delay
dan sebagai konsekuensinya merupakan parameter untuk fungsi-fungsi ini. Delayed<'T>
dapat berupa jenis arbitrer yang tidak perlu dikaitkan dengan M<'T>
; biasanya M<'T>
atau (unit -> M<'T>)
digunakan. Implementasi default-nya adalah M<'T>
. Lihat di sini untuk tampilan yang lebih mendalam.
Pengkompilasi, ketika menguraikan ekspresi komputasi, menerjemahkan ekspresi ke dalam serangkaian panggilan fungsi berlapis dengan menggunakan metode dalam tabel sebelumnya dan kode dalam ekspresi komputasi. Ekspresi berlapis adalah dari bentuk berikut:
builder.Run(builder.Delay(fun () -> {{ cexpr }}))
Dalam kode di atas, panggilan ke Run
dan Delay
dihilangkan jika tidak didefinisikan dalam kelas pembuat ekspresi komputasi. Isi ekspresi komputasi, di sini menunjukkan sebagai {{ cexpr }}
, diterjemahkan ke dalam panggilan lebih lanjut ke metode kelas penyusun. Proses ini didefinisikan secara rekursif sesuai dengan terjemahan dalam tabel berikut. Kode dalam tanda kurung ganda {{ ... }}
tetap harus diterjemahkan, expr
mewakili ekspresi F# dan cexpr
mewakili ekspresi komputasi.
Ekspresi | Terjemahan |
---|---|
{{ let binding in cexpr }} |
let binding in {{ cexpr }} |
{{ let! pattern = expr in cexpr }} |
builder.Bind(expr, (fun pattern -> {{ cexpr }})) |
{{ do! expr in cexpr }} |
builder.Bind(expr, (fun () -> {{ cexpr }})) |
{{ yield expr }} |
builder.Yield(expr) |
{{ yield! expr }} |
builder.YieldFrom(expr) |
{{ return expr }} |
builder.Return(expr) |
{{ return! expr }} |
builder.ReturnFrom(expr) |
{{ use pattern = expr in cexpr }} |
builder.Using(expr, (fun pattern -> {{ cexpr }})) |
{{ use! value = expr in cexpr }} |
builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {{ cexpr }})))) |
{{ if expr then cexpr0 }} |
if expr then {{ cexpr0 }} else builder.Zero() |
{{ if expr then cexpr0 else cexpr1 }} |
if expr then {{ cexpr0 }} else {{ cexpr1 }} |
{{ match expr with | pattern_i -> cexpr_i }} |
match expr with | pattern_i -> {{ cexpr_i }} |
{{ for pattern in enumerable-expr do cexpr }} |
builder.For(enumerable-expr, (fun pattern -> {{ cexpr }})) |
{{ for identifier = expr1 to expr2 do cexpr }} |
builder.For([expr1..expr2], (fun identifier -> {{ cexpr }})) |
{{ while expr do cexpr }} |
builder.While(fun () -> expr, builder.Delay({{ cexpr }})) |
{{ try cexpr with | pattern_i -> expr_i }} |
builder.TryWith(builder.Delay({{ cexpr }}), (fun value -> match value with | pattern_i -> expr_i | exn -> System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exn).Throw())) |
{{ try cexpr finally expr }} |
builder.TryFinally(builder.Delay({{ cexpr }}), (fun () -> expr)) |
{{ cexpr1; cexpr2 }} |
builder.Combine({{ cexpr1 }}, {{ cexpr2 }}) |
{{ other-expr; cexpr }} |
expr; {{ cexpr }} |
{{ other-expr }} |
expr; builder.Zero() |
Dalam tabel sebelumnya, other-expr
menjelaskan ekspresi yang tidak tercantum dalam tabel. Kelas penyusun tidak perlu mengimplementasikan semua metode dan mendukung semua terjemahan yang tercantum dalam tabel sebelumnya. Konstruksi yang tidak diimplementasikan tidak tersedia dalam ekspresi komputasi jenis tersebut. Misalnya, jika Anda tidak ingin mendukung kata kunci use
dalam ekspresi komputasi, Anda dapat menghilangkan definisi Use
di kelas penyusun Anda.
Contoh kode berikut menunjukkan ekspresi komputasi yang merangkum komputasi sebagai serangkaian langkah yang dapat dievaluasi satu langkah pada satu waktu. Jenis gabungan yang didiskriminasi, OkOrException
, mengodekan status kesalahan ekspresi seperti yang dievaluasi sejauh ini. Kode ini menunjukkan beberapa pola tipikal yang dapat Anda gunakan dalam ekspresi komputasi Anda, seperti implementasi boilerplate dari beberapa metode penyusun.
/// Represents computations that can be run step by step
type Eventually<'T> =
| Done of 'T
| NotYetDone of (unit -> Eventually<'T>)
module Eventually =
/// Bind a computation using 'func'.
let rec bind func expr =
match expr with
| Done value -> func value
| NotYetDone work -> NotYetDone (fun () -> bind func (work()))
/// Return the final value
let result value = Done value
/// The catch for the computations. Stitch try/with throughout
/// the computation, and return the overall result as an OkOrException.
let rec catch expr =
match expr with
| Done value -> result (Ok value)
| NotYetDone work ->
NotYetDone (fun () ->
let res = try Ok(work()) with | exn -> Error exn
match res with
| Ok cont -> catch cont // note, a tailcall
| Error exn -> result (Error exn))
/// The delay operator.
let delay func = NotYetDone (fun () -> func())
/// The stepping action for the computations.
let step expr =
match expr with
| Done _ -> expr
| NotYetDone func -> func ()
/// The tryFinally operator.
/// This is boilerplate in terms of "result", "catch", and "bind".
let tryFinally expr compensation =
catch (expr)
|> bind (fun res ->
compensation();
match res with
| Ok value -> result value
| Error exn -> raise exn)
/// The tryWith operator.
/// This is boilerplate in terms of "result", "catch", and "bind".
let tryWith exn handler =
catch exn
|> bind (function Ok value -> result value | Error exn -> handler exn)
/// The whileLoop operator.
/// This is boilerplate in terms of "result" and "bind".
let rec whileLoop pred body =
if pred() then body |> bind (fun _ -> whileLoop pred body)
else result ()
/// The sequential composition operator.
/// This is boilerplate in terms of "result" and "bind".
let combine expr1 expr2 =
expr1 |> bind (fun () -> expr2)
/// The using operator.
/// This is boilerplate in terms of "tryFinally" and "Dispose".
let using (resource: #System.IDisposable) func =
tryFinally (func resource) (fun () -> resource.Dispose())
/// The forLoop operator.
/// This is boilerplate in terms of "catch", "result", and "bind".
let forLoop (collection:seq<_>) func =
let ie = collection.GetEnumerator()
tryFinally
(whileLoop
(fun () -> ie.MoveNext())
(delay (fun () -> let value = ie.Current in func value)))
(fun () -> ie.Dispose())
/// The builder class.
type EventuallyBuilder() =
member x.Bind(comp, func) = Eventually.bind func comp
member x.Return(value) = Eventually.result value
member x.ReturnFrom(value) = value
member x.Combine(expr1, expr2) = Eventually.combine expr1 expr2
member x.Delay(func) = Eventually.delay func
member x.Zero() = Eventually.result ()
member x.TryWith(expr, handler) = Eventually.tryWith expr handler
member x.TryFinally(expr, compensation) = Eventually.tryFinally expr compensation
member x.For(coll:seq<_>, func) = Eventually.forLoop coll func
member x.Using(resource, expr) = Eventually.using resource expr
let eventually = new EventuallyBuilder()
let comp =
eventually {
for x in 1..2 do
printfn $" x = %d{x}"
return 3 + 4
}
/// Try the remaining lines in F# interactive to see how this
/// computation expression works in practice.
let step x = Eventually.step x
// returns "NotYetDone <closure>"
comp |> step
// prints "x = 1"
// returns "NotYetDone <closure>"
comp |> step |> step
// prints "x = 1"
// prints "x = 2"
// returns "Done 7"
comp |> step |> step |> step |> step
Ekspresi komputasi memiliki tipe yang mendasarinya, yang dikembalikan oleh ekspresi. Jenis yang mendasari dapat mewakili hasil yang dihitung atau perhitungan tertunda yang dapat dilakukan, atau dapat memberikan cara untuk beralih melalui beberapa jenis kumpulan. Dalam contoh sebelumnya, jenis yang mendasar adalah Eventually<_>
. Untuk ekspresi urutan, jenis yang mendasarinya adalah System.Collections.Generic.IEnumerable<T>. Untuk ekspresi kueri, jenis yang mendasarinya adalah System.Linq.IQueryable. Untuk ekspresi asinkron, jenis yang mendasarinya adalah Async
. Objek Async
mewakili pekerjaan yang harus dilakukan untuk menghitung hasilnya. Misalnya, Anda memanggil Async.RunSynchronously
untuk menjalankan komputasi dan mengembalikan hasilnya.
Operasi Kustom
Anda dapat menentukan operasi kustom pada ekspresi komputasi dan menggunakan operasi kustom sebagai operator dalam ekspresi komputasi. Misalnya, Anda bisa menyertakan operator kueri dalam ekspresi kueri. Saat Anda menentukan operasi kustom, Anda harus menentukan metode Yield dan For dalam ekspresi komputasi. Untuk menentukan operasi kustom, letakkan di kelas penyusun untuk ekspresi komputasi, lalu terapkan CustomOperationAttribute
. Atribut ini mengambil string sebagai argumen, yang merupakan nama yang akan digunakan dalam operasi kustom. Nama ini masuk ke dalam cakupan di awal kurung kurawal pembuka ekspresi komputasi. Oleh karena itu, Anda tidak boleh menggunakan pengidentifikasi yang memiliki nama yang sama dengan operasi kustom di blok ini. Misalnya, hindari penggunaan pengidentifikasi seperti all
atau last
dalam ekspresi kueri.
Memperluas Penyusun yang ada dengan Operasi Kustom baru
Jika Anda sudah memiliki kelas penyusun, operasi kustomnya dapat diperluas dari luar kelas penyusun ini. Ekstensi harus dideklarasikan dalam modul. Namespace layanan tidak boleh berisi anggota ekstensi kecuali dalam file yang sama dan grup deklarasi namespace layanan yang sama tempat jenis ditentukan.
Contoh berikut menunjukkan ekstensi kelas FSharp.Linq.QueryBuilder
yang ada.
open System
open FSharp.Linq
type QueryBuilder with
[<CustomOperation("existsNot")>]
member _.ExistsNot (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.Any (source.Source, Func<_,_>(predicate)) |> not
Operasi kustom dapat overload. Untuk informasi selengkapnya, lihat F# RFC FS-1056 - Izinkan overload kata kunci kustom dalam ekspresi komputasi.
Mengompilasi ekspresi komputasi secara efisien
Ekspresi komputasi F# yang menangguhkan eksekusi dapat dikompilasi ke mesin status yang sangat efisien melalui penggunaan fitur tingkat rendah yang cermat yang disebut kode yang dapat dilanjutkan. Kode yang dapat dilanjutkan didokumentasikan dalam F# RFC FS-1087 dan digunakan untuk Ekspresi Tugas.
Ekspresi komputasi F# yang sinkron (yaitu, mereka tidak menangguhkan eksekusi) dapat dikompilasi ke mesin status yang efisien dengan menggunakan fungsi sebaris termasuk atribut InlineIfLambda
. Contoh diberikan dalam F# RFC FS-1098.
Ekspresi daftar, ekspresi array, dan ekspresi urutan diberikan perlakuan khusus oleh pengompilasi F# untuk memastikan pembuatan kode beperforma tinggi.
Lihat juga
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk