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> atau

M<'T> -> 'T
Menjalankan ekspresi komputasi.
Combine M<'T> * Delayed<'T> -> M<'T> atau

M<unit> * M<'T> -> M<'T>
Dipanggil untuk pengurutan dalam ekspresi komputasi.
For seq<'T> * ('T -> M<'U>) -> M<'U> atau

seq<'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