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.
Ekspresi komputasi dalam F# memberikan sintaks yang nyaman untuk menulis komputasi yang dapat diurutkan dan dikombinasikan menggunakan konstruksi dan pengikatan alur kontrol. Tergantung pada 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 terkait 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 bentuk. 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 meliputi:
- Komputasi non-deterministik
- Komputasi asinkron
- Komputasi yang efektif
- Komputasi generatif
Lebih umum, ada komputasi sensitif konteks yang harus Anda lakukan di bagian tertentu dari aplikasi. Menulis kode sensitif konteks bisa menjadi tantangan, 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 Tipe Ekspresi Komputasi Baru, yang memperlihatkan cara membuat ekspresi komputasi kustom.
Gambaran umum sintaks
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 ekspresi ekspresi komputasi. Misalnya, async kode ekspresi komputasi 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 kata kunci tersebut telah ditentukan dalam jenis penyusun backing. Satu-satunya pengecualian untuk ini adalah match!, yang merupakan gula sintaktik sendiri 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 mendeskripsikan kelas builder adalah dengan mengatakan bahwa ini memungkinkan Anda untuk menyesuaikan operasi berbagai konstruk F#, seperti perulangan dan pengikatan.
let!
Kata kunci let! mengikat hasil dari panggilan pada ekspresi komputasi lain ke sebuah 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 yang tidak direalisasi ke ekspresi komputasi tersebut. Gunakan let! untuk mengikat hasilnya.
let! ditentukan oleh anggota Bind(x, f) pada tipe pembangun.
and!
Kata and! kunci memungkinkan Anda untuk mengikat hasil beberapa panggilan ekspresi komputasi secara lebih efisien. Kata kunci ini memungkinkan ekspresi komputasi aplikatif, yang menyediakan model komputasi yang berbeda dari pendekatan monadik standar.
let doThingsAsync url =
async {
let! data = getDataAsync url
and! moreData = getMoreDataAsync anotherUrl
and! evenMoreData = getEvenMoreDataAsync someUrl
...
}
Menggunakan serangkaian let! ... let! ... eksekusi komputasi secara berurutan, bahkan jika mereka independen. Sebaliknya, let! ... and! ... menunjukkan bahwa komputasi independen, memungkinkan kombinasi aplikatif. Kemandirian ini memungkinkan penulis ekspresi komputasi untuk:
- Jalankan komputasi dengan lebih efisien.
- Dapat menjalankan komputasi secara paralel.
- Mengakumulasi hasil tanpa dependensi berurutan yang tidak perlu.
Pembatasannya adalah bahwa komputasi yang dikombinasikan dengan and! tidak dapat bergantung pada hasil nilai yang terikat sebelumnya dalam rantai yang samalet!/and!. Trade-off ini memungkinkan keuntungan performa.
and! didefinisikan terutama oleh anggota MergeSources(x1, x2) dari tipe pembuat.
Secara opsional, MergeSourcesN(x1, x2 ..., xN) dapat didefinisikan untuk mengurangi jumlah simpul tupel, dan BindN(x1, x2 ..., xN, f), atau BindNReturn(x1, x2, ..., xN, f) dapat didefinisikan untuk menghubungkan hasil ekspresi komputasi secara efisien tanpa simpul tupel.
Untuk informasi selengkapnya tentang ekspresi komputasi aplikatif, lihat Ekspresi Komputasi Aplikasi di F# 5 dan F# RFC FS-1063.
do!
Kata kunci do! adalah untuk memanggil ekspresi komputasi yang mengembalikan jenis unit-like (didefinisikan oleh anggota Zero pada pembangun):
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 Bind(x, f) anggota pada jenis penyusun, di mana f menghasilkan unit.
yield
Kata yield kunci 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 penelepon. 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 dilakukan:
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 diulang.
yield didefinisikan oleh anggota pembangun Yield(x) pada tipe pembangun, di mana x adalah item yang akan dikembalikan.
yield!
Kata yield! kuncinya 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
Ketika dievaluasi, ekspresi komputasi yang dipanggil oleh yield! akan mengembalikan itemnya satu per satu, menyederhanakan hasilnya.
yield! didefinisikan oleh YieldFrom(x) anggota pada jenis penyusun, di mana x adalah kumpulan nilai.
Tidak seperti yield, yield! harus ditentukan secara eksplisit. Perilakunya tidak implisit dalam ekspresi komputasi.
return
Kata return kunci membungkus nilai dalam jenis yang sesuai dengan ekspresi komputasi. Selain ekspresi komputasi menggunakan yield, ini digunakan untuk "menyelesaikan" ekspresi tersebut.
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 didefinisikan oleh anggota Return(x) pada tipe pembuat, di mana x adalah item yang akan dibungkus. Untuk let! ... return penggunaan, BindReturn(x, f) dapat digunakan untuk meningkatkan performa.
return!
Kata kunci return! merealisasikan nilai dari ekspresi komputasi dan membungkus hasil tersebut dalam 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! didefinisikan oleh anggota ReturnFrom(x) dari tipe pembangun, di mana x merupakan ekspresi komputasi lainnya.
match!
Kata match! kunci memungkinkan Anda untuk melakukan panggilan ke ekspresi komputasi lain secara sebaris dan mencocokkan 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# menentukan 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 dalam kelas penyusun alur kerja.
| Metode | Tanda tangan umum | Deskripsi |
|---|---|---|
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 let! dan and! yang efisien dalam ekspresi komputasi tanpa menggabungkan 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. |
ReturnFromFinal |
M<'T> -> M<'T> |
Jika ada, dipanggil untuk return! dan do! ketika dalam posisi panggilan ekor. |
BindReturn |
(M<'T1> * ('T1 -> 'T2)) -> M<'T2> |
Memerlukan pemanfaatan yang efisien let! ... return dalam ekspresi komputasi. |
BindNReturn |
(M<'T1> * M<'T2> * ... * M<'TN> * ('T1 * 'T2 ... * 'TN -> M<'U>)) -> M<'U> |
Diperlukan efisiensi 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 proses dengan mengurangi jumlah simpul perangkaian.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 for...do ekspresi dalam ekspresi komputasi. |
TryFinally |
Delayed<'T> * (unit -> unit) -> M<'T> |
Dipanggil untuk try...finally ekspresi dalam ekspresi komputasi. |
TryWith |
Delayed<'T> * (exn -> M<'T>) -> M<'T> |
Dipanggil untuk try...with ekspresi dalam ekspresi komputasi. |
Using |
'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable |
Dipanggil untuk use pengaitan dalam ekspresi komputasi. |
While |
(unit -> bool) * Delayed<'T> -> M<'T>atau(unit -> bool) * Delayed<unit> -> M<unit> |
Dipanggil untuk while...do ekspresi dalam ekspresi komputasi. |
Yield |
'T -> M<'T> |
Dipanggil untuk yield ekspresi dalam ekspresi komputasi. |
YieldFrom |
M<'T> -> M<'T> |
Dipanggil untuk yield! ekspresi dalam ekspresi komputasi. |
YieldFromFinal |
M<'T> -> M<'T> |
Jika ada, dipanggil yield! ketika dalam posisi panggilan ekor dan dalam kasus do! dalam posisi panggilan ekor sebagai fallback untuk ReturnFromFinal |
Zero |
unit -> M<'T> |
Dipanggil untuk cabang kosong else dari ekspresi if...then dalam ekspresi komputasi. |
Quote |
Quotations.Expr<'T> -> Quotations.Expr<'T> |
Menunjukkan bahwa ekspresi komputasi diteruskan ke anggota Run sebagai kuotasi. Ini menerjemahkan semua instans komputasi ke dalam kutipan. |
Banyak metode dalam kelas penyusun menggunakan dan mengembalikan M<'T> konstruksi, 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 metode ini memungkinkannya digabungkan dan disarangkan satu sama lain, sehingga objek alur kerja yang dikembalikan dari satu konstruksi dapat diteruskan ke konstruksi berikutnya.
Banyak fungsi menggunakan hasil Delay sebagai argumen: Run, , While, TryWithTryFinally, dan Combine. Tipe Delayed<'T> adalah tipe pengembalian dari Delay dan karenanya menjadi parameter bagi fungsi-fungsi ini.
Delayed<'T> bisa menjadi jenis arbitrer yang tidak perlu terkait dengan M<'T>; umumnya M<'T> atau (unit -> M<'T>) digunakan. Implementasi defaultnya adalah M<'T>. Untuk tampilan yang lebih mendalam, lihat Memahami batasan jenis.
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 memiliki bentuk sebagai berikut:
builder.Run(builder.Delay(fun () -> {{ cexpr }}))
Dalam kode di atas, panggilan ke Run dan Delay dihilangkan jika tidak ditentukan dalam kelas penyusun ekspresi komputasi. Isi ekspresi komputasi, di sini ditandai sebagai {{ cexpr }}, diterjemahkan ke dalam panggilan selanjutnya ke metode kelas pembangun. 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 use kata kunci dalam ekspresi penghitungan, Anda dapat menghilangkan definisi Use di kelas pembangun Anda.
Contoh kode berikut menunjukkan ekspresi komputasi yang merangkum komputasi sebagai serangkaian langkah yang dapat dievaluasi satu langkah pada satu waktu. Union bertipe diskret, OkOrException, mengenkode status kesalahan ekspresi berdasarkan evaluasi sejauh ini. Kode ini menunjukkan beberapa pola khas 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 jenis dasar yang menjadi hasil dari ekspresi tersebut. Jenis yang mendasar dapat mewakili hasil komputasi atau komputasi tertunda yang dapat dilakukan, atau mungkin memberikan cara untuk melakukan iterasi melalui beberapa jenis koleksi. Dalam contoh sebelumnya, jenis yang mendasar adalah Eventually<_>. Untuk ekspresi urutan, tipe dasar adalah System.Collections.Generic.IEnumerable<T>. Untuk ekspresi kueri, jenis yang mendasar adalah System.Linq.IQueryable. Untuk ekspresi asinkron, jenis yang mendasar adalah Async. Objek Async mewakili pekerjaan yang akan 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 Hasil dan Untuk 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 pada 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 Pembuat 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 tidak boleh berisi anggota ekstensi kecuali dalam file yang sama dan grup deklarasi namespace yang sama tempat jenis ditentukan.
Contoh berikut menunjukkan ekstensi kelas yang ada FSharp.Linq.QueryBuilder .
open System
open FSharp.Linq
type QueryBuilder with
[<CustomOperation>]
member _.any (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.Any (source.Source, Func<_,_>(predicate))
[<CustomOperation("singleSafe")>] // you can specify your own operation name in the constructor
member _.singleOrDefault (source: QuerySource<'T, 'Q>, predicate) =
System.Linq.Enumerable.SingleOrDefault (source.Source, Func<_,_>(predicate))
Operasi kustom dapat kelebihan beban. Untuk informasi selengkapnya, lihat F# RFC FS-1056 - Mengizinkan kelebihan beban kata kunci kustom dalam ekspresi komputasi.
Mengkompilasi ekspresi komputasi secara efisien
Ekspresi komputasi F# yang menangguhkan eksekusi dapat dikompilasi ke komputer status yang sangat efisien melalui penggunaan fitur tingkat rendah yang hati-hati yang disebut kode yang dapat dilanjutkan. Kode yang dapat dilanjutkan didokumenkan dalam F# RFC FS-1087 dan digunakan untuk Ekspresi Tugas.
Ekspresi komputasi F# yang sinkron (yaitu, mereka tidak menangguhkan eksekusi) dapat dikompilasi menjadi mesin status yang lebih efisien dengan menggunakan fungsi sebaris dengan menyertakan atribut InlineIfLambda. Contoh diberikan dalam F# RFC FS-1098.
Ekspresi daftar, ekspresi array, dan ekspresi urutan diberikan perlakuan khusus oleh pengkompilasi F# untuk memastikan pembuatan kode berkinerja tinggi.