Hesaplama İfadeleriComputation Expressions

F#'daki hesaplama ifadeleri, denetim akışı yapıları ve bağlamaları kullanılarak sıralanabilen ve birleştirilebilen hesaplamalar yazmak için kullanışlı bir sözdizimi sağlar.Computation expressions in F# provide a convenient syntax for writing computations that can be sequenced and combined using control flow constructs and bindings. Hesaplama ifade türüne bağlı olarak, onlar monads, monoids, monad transformatörler ve aplikatif functors ifade etmek için bir yol olarak düşünülebilir.Depending on the kind of computation expression, they can be thought of as a way to express monads, monoids, monad transformers, and applicative functors. Ancak, diğer dillerin (Haskell'daki do-notation gibi) aksine, tek bir soyutlamayla bağlı değildirler ve uygun ve içeriğe duyarlı bir sözdizimini gerçekleştirmek için makrolara veya diğer meta programlama biçimlerine güvenmezler.However, unlike other languages (such as do-notation in Haskell), they are not tied to a single abstraction, and do not rely on macros or other forms of metaprogramming to accomplish a convenient and context-sensitive syntax.

Genel BakışOverview

Hesaplamalar çeşitli şekillerde olabilir.Computations can take many forms. En yaygın hesaplama biçimi, anlaşılması ve değiştirilmesi kolay tek iş parçacığı yürütmedir.The most common form of computation is single-threaded execution, which is easy to understand and modify. Ancak, tüm hesaplama biçimleri tek iş parçacığı yürütme kadar basit değildir.However, not all forms of computation are as straightforward as single-threaded execution. Bazı örnekler:Some examples include:

  • Deterministik olmayan hesaplamalarNon-deterministic computations
  • Asynchronous hesaplamalarıAsynchronous computations
  • Etkili hesaplamalarEffectful computations
  • Üretici hesaplamalarıGenerative computations

Daha genel olarak, bir uygulamanın belirli bölümlerinde gerçekleştirmeniz gereken içeriğe duyarlı hesaplamalar vardır.More generally, there are context-sensitive computations that you must perform in certain parts of an application. İçeriğe duyarlı kod yazmak zor olabilir, çünkü bunu yapmanızı engellemek için soyutlamalar olmadan belirli bir bağlamın dışında hesaplamaları "sızdırmak" kolaydır.Writing context-sensitive code can be challenging, as it is easy to "leak" computations outside of a given context without abstractions to prevent you from doing so. Bu soyutlamalar genellikle kendiniz yazmak zor, bu yüzden F # sözde hesaplama ifadeleriyapmak için genelleştirilmiş bir yolu vardır.These abstractions are often challenging to write by yourself, which is why F# has a generalized way to do so called computation expressions.

Hesaplama ifadeleri, içeriğe duyarlı hesaplamaları kodlamak için tek biçimli bir sözdizimi ve soyutlama modeli sunar.Computation expressions offer a uniform syntax and abstraction model for encoding context-sensitive computations.

Her hesaplama ifadesi bir oluşturucu türü tarafından desteklenen.Every computation expression is backed by a builder type. Oluşturucu türü, hesaplama ifadesi için kullanılabilen işlemleri tanımlar.The builder type defines the operations that are available for the computation expression. Bkz. Özel bir hesaplama ifadesinin nasıl oluşturulup oluşturulturunu gösteren Yeni Bir Hesaplama İfadesi Türü Oluşturma.See Creating a New Type of Computation Expression, which shows how to create a custom computation expression.

Sözdizimine genel bakışSyntax overview

Tüm hesaplama ifadeleri aşağıdaki formu vardır:All computation expressions have the following form:

builder-expr { cexper }

nerede builder-expr hesaplama ifadesini tanımlayan bir oluşturucu türünün adıdır cexper ve hesaplama ifadesinin ifade gövdesidir.where builder-expr is the name of a builder type that defines the computation expression, and cexper is the expression body of the computation expression. Örneğin, async hesaplama ifade kodu şu şekilde görünebilir:For example, async computation expression code can look like this:

let fetchAndDownload url =
    async {
        let! data = downloadData url

        let processedData = processData data

        return processedData
    }

Önceki örnekte gösterildiği gibi, bir hesaplama ifadesi içinde özel, ek sözdizimi vardır.There is a special, additional syntax available within a computation expression, as shown in the previous example. Aşağıdaki ifade formları hesaplama ifadeleri ile mümkündür:The following expression forms are possible with computation expressions:

expr { let! ... }
expr { do! ... }
expr { yield ... }
expr { yield! ... }
expr { return ... }
expr { return! ... }
expr { match! ... }

Bu anahtar kelimelerin her biri ve diğer standart F# anahtar kelimeleri yalnızca destek oluşturucu türünde tanımlanmışsa, bir hesaplama ifadesinde kullanılabilir.Each of these keywords, and other standard F# keywords are only available in a computation expression if they have been defined in the backing builder type. Bunun tek match!istisnası, kendisi sonucu bir desen maç let! takip kullanımı için sözdizimli şeker olmasıdır.The only exception to this is match!, which is itself syntactic sugar for the use of let! followed by a pattern match on the result.

Oluşturucu türü, hesaplama ifadesinin parçalarının birleştirilmesi yöntemini yöneten özel yöntemleri tanımlayan bir nesnedir; diğer bir de, yöntemleri hesaplama ifadesinin nasıl olacağını denetler.The builder type is an object that defines special methods that govern the way the fragments of the computation expression are combined; that is, its methods control how the computation expression behaves. Oluşturucu sınıfını tanımlamanın başka bir yolu da, döngüler ve bağlamalar gibi birçok F# yapısının çalışmasını özelleştirmenize olanak sağladığını söylemektir.Another way to describe a builder class is to say that it enables you to customize the operation of many F# constructs, such as loops and bindings.

let!

Anahtar let! kelime, bir ada yapılan bir çağrının sonucunu başka bir hesaplama ifadesine bağlar:The let! keyword binds the result of a call to another computation expression to a name:

let doThingsAsync url =
    async {
        let! data = getDataAsync url
        ...
    }

Aramayı bir hesaplama ifadesine letbağlarsanız, hesaplama ifadesinin sonucunu alamazsınız.If you bind the call to a computation expression with let, you will not get the result of the computation expression. Bunun yerine, gerçekleşmemiş çağrının değerini bu hesaplama ifadesine bağlamış olursunuz.Instead, you will have bound the value of the unrealized call to that computation expression. Sonuca let! bağlamak için kullanın.Use let! to bind to the result.

let!oluşturucu türünde Bind(x, f) üye tarafından tanımlanır.let! is defined by the Bind(x, f) member on the builder type.

do!

Anahtar do! kelime, (oluşturucudaki Zero üye tarafından unittanımlanan) benzer bir türü döndüren bir hesaplama ifadesini çağırmak içindir:The do! keyword is for calling a computation expression that returns a unit-like type (defined by the Zero member on the builder):

let doThingsAsync data url =
    async {
        do! submitData data url
        ...
    }

Async iş akışıiçin, Async<unit>bu tür .For the async workflow, this type is Async<unit>. Diğer hesaplama ifadeleri için, tür CExpType<unit>.For other computation expressions, the type is likely to be CExpType<unit>.

do!oluşturucu türünde Bind(x, f) üye tarafından tanımlanır, burada f bir unit.do! is defined by the Bind(x, f) member on the builder type, where f produces a unit.

yield

Anahtar yield kelime, hesaplama ifadesinden bir değeri döndürmeiçindir, böylece aşağıdaki IEnumerable<T>ler olarak tüketilebilir:The yield keyword is for returning a value from the computation expression so that it can be consumed as an IEnumerable<T>:

let squares =
    seq {
        for i in 1..10 do
            yield i * i
    }

for sq in squares do
    printfn "%d" sq

Çoğu durumda, arayanlar tarafından atlanabilir.In most cases, it can be omitted by callers. Atlatır'ın yield en yaygın yolu -> işleçtir:The most common way to omit yield is with the -> operator:

let squares =
    seq {
        for i in 1..10 -> i * i
    }

for sq in squares do
    printfn "%d" sq

Birçok farklı değer verebilecek daha karmaşık ifadeler için ve belki de koşullu olarak, anahtar sözcüğü atlayarak şunları yapabilir:For more complex expressions that might yield many different values, and perhaps conditionally, simply omitting the keyword can do:

let weekdays includeWeekend =
    seq {
        "Monday"
        "Tuesday"
        "Wednesday"
        "Thursday"
        "Friday"
        if includeWeekend then
            "Saturday"
            "Sunday"
    }

C#'daki verim anahtar sözcüğündeolduğu gibi, hesaplama ifadesindeki her öğe, yinelenirken geri verilir.As with the yield keyword in C#, each element in the computation expression is yielded back as it is iterated.

yieldoluşturucu türünde Yield(x) üye tarafından tanımlanır, geri verim için öğe nerede. xyield is defined by the Yield(x) member on the builder type, where x is the item to yield back.

yield!

Anahtar yield! kelime, bir hesaplama ifadesinden değerler koleksiyonunu düzleştirmek içindir:The yield! keyword is for flattening a collection of values from a computation expression:

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 "%A" squaresAndCubes // Prints - 1; 4; 9; 1; 8; 27

Değerlendirildiğinde, tarafından yield! çağrılan hesaplama ifadesi, öğelerini birer birer geri vererek sonucu düzleştirmek zorunda kalarak elde edilir.When evaluated, the computation expression called by yield! will have its items yielded back one-by-one, flattening the result.

yield!değer koleksiyonu YieldFrom(x) nun bulunduğu x oluşturucu türündeki üye tarafından tanımlanır.yield! is defined by the YieldFrom(x) member on the builder type, where x is a collection of values.

yieldAksine, yield! açıkça belirtilmelidir.Unlike yield, yield! must be explicitly specified. Davranışı hesaplama ifadelerinde örtük değildir.Its behavior isn't implicit in computation expressions.

return

return Anahtar kelime, hesaplama ifadesine karşılık gelen türdeki bir değeri sarar.The return keyword wraps a value in the type corresponding to the computation expression. Bir hesaplama ifadesini yield"tamamlamak" için kullanılır:Aside from computation expressions using yield, it is used to "complete" a computation expression:

let req = // 'req' is of type is 'Async<data>'
    async {
        let! data = fetch url
        return data
    }

// 'result' is of type 'data'
let result = Async.RunSynchronously req

returnoluşturucu türünde Return(x) üye tarafından tanımlanır, nerede x kaydırmak için öğedir.return is defined by the Return(x) member on the builder type, where x is the item to wrap.

return!

return! Anahtar kelime bir hesaplama ifadesinin değerini fark eder ve hesaplama ifadesine karşılık gelen türle sonuçlanan sarar:The return! keyword realizes the value of a computation expression and wraps that result in the type corresponding to the computation expression:

let req = // 'req' is of type is 'Async<data>'
    async {
        return! fetch url
    }

// 'result' is of type 'data'
let result = Async.RunSynchronously req

return!oluşturucu türünde ReturnFrom(x) üye tarafından tanımlanır, başka bir hesaplama ifadesi nerede. xreturn! is defined by the ReturnFrom(x) member on the builder type, where x is another computation expression.

match!

Anahtar match! kelime, sonucu yla ilgili başka bir hesaplama ifadesi ve desen eşleşmesine bir çağrı nın satır satırını koymanızı sağlar:The match! keyword allows you to inline a call to another computation expression and pattern match on its result:

let doThingsAsync url =
    async {
        match! callService url with
        | Some data -> ...
        | None -> ...
    }

Bir hesaplama ifadesini çağırırken match!, '' gibi let!çağrının sonucunu fark edecektir.When calling a computation expression with match!, it will realize the result of the call like let!. Bu genellikle sonucun isteğe bağlıolduğu bir hesaplama ifadesini ararken kullanılır.This is often used when calling a computation expression where the result is an optional.

Yerleşik hesaplama ifadeleriBuilt-in computation expressions

F# çekirdek kitaplığı üç yerleşik hesaplama deyimi tanımlar: Sıralı İfadeler, Eşzamanlı İş Akışlarıve Sorgu İfadeleri.The F# core library defines three built-in computation expressions: Sequence Expressions, Asynchronous Workflows, and Query Expressions.

Yeni Bir Hesaplama İfadesi Türü OluşturmaCreating a New Type of Computation Expression

Bir oluşturucu sınıf oluşturarak ve sınıfta belirli özel yöntemleri tanımlayarak kendi hesaplama ifadelerinizin özelliklerini tanımlayabilirsiniz.You can define the characteristics of your own computation expressions by creating a builder class and defining certain special methods on the class. Oluşturucu sınıf isteğe bağlı olarak aşağıdaki tabloda listelenen yöntemleri tanımlayabilir.The builder class can optionally define the methods as listed in the following table.

Aşağıdaki tabloda iş akışı oluşturucu sınıfında kullanılabilecek yöntemler açıklanmaktadır.The following table describes methods that can be used in a workflow builder class.

YöntemMethod Tipik imza(lar)Typical signature(s) AçıklamaDescription
Bind M<'T> * ('T -> M<'U>) -> M<'U> Çağrılan let! ve do! hesaplama ifadeleri.Called for let! and do! in computation expressions.
Delay (unit -> M<'T>) -> M<'T> Bir hesaplama ifadesini işlev olarak sarar.Wraps a computation expression as a function.
Return 'T -> M<'T> Hesaplama return ifadelerinde çağrıldı.Called for return in computation expressions.
ReturnFrom M<'T> -> M<'T> Hesaplama return! ifadelerinde çağrıldı.Called for return! in computation expressions.
Run M<'T> -> M<'T> veyaM<'T> -> M<'T> or

M<'T> -> 'T
Bir hesaplama ifadesini yürütür.Executes a computation expression.
Combine M<'T> * M<'T> -> M<'T> veyaM<'T> * M<'T> -> M<'T> or

M<unit> * M<'T> -> M<'T>
Hesaplama ifadelerinde sıralama için çağrıldı.Called for sequencing in computation expressions.
For seq<'T> * ('T -> M<'U>) -> M<'U> veyaseq<'T> * ('T -> M<'U>) -> M<'U> or

seq<'T> * ('T -> M<'U>) -> seq<M<'U>>
Hesaplama for...do ifadelerinde ifadeler için çağrıldı.Called for for...do expressions in computation expressions.
TryFinally M<'T> * (unit -> unit) -> M<'T> Hesaplama try...finally ifadelerinde ifadeler için çağrıldı.Called for try...finally expressions in computation expressions.
TryWith M<'T> * (exn -> M<'T>) -> M<'T> Hesaplama try...with ifadelerinde ifadeler için çağrıldı.Called for try...with expressions in computation expressions.
Using 'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable Hesaplama use ifadelerinde bağlama lar için çağrıda bulunuldu.Called for use bindings in computation expressions.
While (unit -> bool) * M<'T> -> M<'T> Hesaplama while...do ifadelerinde ifadeler için çağrıldı.Called for while...do expressions in computation expressions.
Yield 'T -> M<'T> Hesaplama yield ifadelerinde ifadeler için çağrıldı.Called for yield expressions in computation expressions.
YieldFrom M<'T> -> M<'T> Hesaplama yield! ifadelerinde ifadeler için çağrıldı.Called for yield! expressions in computation expressions.
Zero unit -> M<'T> Hesaplama ifadelerinde else if...then ifadelerin boş dalları için çağrıda bulundu.Called for empty else branches of if...then expressions in computation expressions.
Quote Quotations.Expr<'T> -> Quotations.Expr<'T> Hesaplama ifadesinin Run üyeye tırnak olarak geçirildiğini gösterir.Indicates that the computation expression is passed to the Run member as a quotation. Bir hesaplamanın tüm örneklerini bir teklife çevirir.It translates all instances of a computation into a quotation.

Oluşturucu sınıfındaki yöntemlerin çoğu, M<'T> genellikle, örneğin eşzamanlı iş akışları ve dizi iş akışları Async<'T> için birleştirilen hesaplama türünü karakterize eden ayrı Seq<'T> tanımlanmış bir tür olan bir yapıyı kullanır ve döndürer.Many of the methods in a builder class use and return an M<'T> construct, which is typically a separately defined type that characterizes the kind of computations being combined, for example, Async<'T> for asynchronous workflows and Seq<'T> for sequence workflows. Bu yöntemlerin imzaları, bir yapıdan döndürülen iş akışı nesnesinin bir sonrakine geçirilebilmesini sağlamak için birbiriyle birleştirilmesini ve iç içe geçmelerini sağlar.The signatures of these methods enable them to be combined and nested with each other, so that the workflow object returned from one construct can be passed to the next. Derleyici, bir hesaplama ifadesini ayrıştırdığında, önceki tablodaki yöntemleri ve hesaplama ifadesindeki kodu kullanarak ifadeyi iç içe geçmiş işlev çağrıları dizisine dönüştürür.The compiler, when it parses a computation expression, converts the expression into a series of nested function calls by using the methods in the preceding table and the code in the computation expression.

İç içe ifade aşağıdaki formdadır:The nested expression is of the following form:

builder.Run(builder.Delay(fun () -> {| cexpr |}))

Yukarıdaki kodda, hesaplama Run ifade Delay oluşturucu sınıfında tanımlanmamışsa yapılan çağrılar atlanır ve atlanır.In the above code, the calls to Run and Delay are omitted if they are not defined in the computation expression builder class. Hesaplama ifadesinin gövdesi, burada belirtildiği gibi, {| cexpr |}aşağıdaki tabloda açıklanan çeviriler tarafından oluşturucu sınıfın yöntemlerini içeren çağrılara çevrilir.The body of the computation expression, here denoted as {| cexpr |}, is translated into calls involving the methods of the builder class by the translations described in the following table. Hesaplama ifadesi, {| cexpr |} F# ifadesinin bulunduğu expr ve cexpr bir hesaplama ifadesi olduğu bu çevirilere göre özyinelemeli olarak tanımlanır.The computation expression {| cexpr |} is defined recursively according to these translations where expr is an F# expression and cexpr is a computation expression.

İfadelerExpression ÇeviriTranslation
{ 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 expr do cexpr } builder.For(enumeration, (fun pattern -> { cexpr }))
{ for identifier = expr1 to expr2 do cexpr } builder.For(enumeration, (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 -> reraise exn)))
{ 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()

Önceki tabloda, other-expr tabloda başka türlü listelenmemiş bir ifade açıklanır.In the previous table, other-expr describes an expression that is not otherwise listed in the table. Oluşturucu sınıfın tüm yöntemleri uygulaması ve önceki tabloda listelenen tüm çevirileri desteklemesi gerekmez.A builder class does not need to implement all of the methods and support all of the translations listed in the previous table. Uygulanmayan yapılar, bu tür hesaplama ifadelerinde kullanılamaz.Those constructs that are not implemented are not available in computation expressions of that type. Örneğin, hesaplama ifadelerinizdeki anahtar kelimeyi use desteklemek istemiyorsanız, oluşturucu sınıfınızdaki tanımı Use atlayabilirsiniz.For example, if you do not want to support the use keyword in your computation expressions, you can omit the definition of Use in your builder class.

Aşağıdaki kod örneği, bir hesaplamayı bir adım adım değerlendirilebilecek bir dizi adım olarak kapsülleyen bir hesaplama ifadesini gösterir.The following code example shows a computation expression that encapsulates a computation as a series of steps that can be evaluated one step at a time. Ayrımcı bir birlik OkOrExceptiontürü, şimdiye kadar değerlendirildiği ifadenin hata durumunu kodlar.A discriminated union type, OkOrException, encodes the error state of the expression as evaluated so far. Bu kod, bazı oluşturucu yöntemlerinin ortak uygulama uygulamaları gibi hesaplama ifadelerinizde kullanabileceğiniz birkaç tipik desen gösterir.This code demonstrates several typical patterns that you can use in your computation expressions, such as boilerplate implementations of some of the builder methods.

// Computations that can be run step by step
type Eventually<'T> =
    | Done of 'T
    | NotYetDone of (unit -> Eventually<'T>)

module Eventually =
    // The bind for the computations. Append 'func' to the
    // computation.
    let rec bind func expr =
        match expr with
        | Done value -> func value
        | NotYetDone work -> NotYetDone (fun () -> bind func (work()))

    // Return the final value wrapped in the Eventually type.
    let result value = Done value

    type OkOrException<'T> =
        | Ok of 'T
        | Exception of System.Exception

    // 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 -> Exception exn
                match res with
                | Ok cont -> catch cont // note, a tailcall
                | Exception exn -> result (Exception 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 rest of the operations are boilerplate.
    // 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
            | Exception 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 | Exception 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.
    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

Bir hesaplama ifadesi, ifadenin döndürdüğü altta yatan bir türü vardır.A computation expression has an underlying type, which the expression returns. Temel tür, hesaplanmış bir sonucu veya gerçekleştirilebilecek gecikmiş bir hesaplamayı temsil edebilir veya bir tür koleksiyon aracılığıyla yinelenmenin bir yolunu sağlayabilir.The underlying type may represent a computed result or a delayed computation that can be performed, or it may provide a way to iterate through some type of collection. Önceki örnekte, altta yatan tür Sonundaoldu.In the previous example, the underlying type was Eventually. Bir sıra ifade için, System.Collections.Generic.IEnumerable<T>temel türü .For a sequence expression, the underlying type is System.Collections.Generic.IEnumerable<T>. Sorgu ifadesi için, temel System.Linq.IQueryabletür .For a query expression, the underlying type is System.Linq.IQueryable. Eşzamanlı bir iş akışı için, temel Asynctür .For an asynchronous workflow, the underlying type is Async. Nesne, Async sonucu hesaplamak için gerçekleştirilecek çalışmayı temsil eder.The Async object represents the work to be performed to compute the result. Örneğin, bir Async.RunSynchronously hesaplama yürütmek ve sonucu döndürmek için arayın.For example, you call Async.RunSynchronously to execute a computation and return the result.

Özel İşlemlerCustom Operations

Bir hesaplama ifadesinde özel bir işlem tanımlayabilir ve bir hesaplama ifadesinde işleç olarak özel bir işlem kullanabilirsiniz.You can define a custom operation on a computation expression and use a custom operation as an operator in a computation expression. Örneğin, sorgu ifadesine bir sorgu işleci ekleyebilirsiniz.For example, you can include a query operator in a query expression. Özel bir işlem tanımladığınızda, hesaplama ifadesinde Verim ve For yöntemlerini tanımlamanız gerekir.When you define a custom operation, you must define the Yield and For methods in the computation expression. Özel bir işlem tanımlamak için, hesaplama ifadesi için bir oluşturucu sınıfına koyun ve sonra CustomOperationAttribute.To define a custom operation, put it in a builder class for the computation expression, and then apply the CustomOperationAttribute. Bu öznitelik, özel bir işlemde kullanılacak ad olan bir bağımsız değişken olarak bir dize alır.This attribute takes a string as an argument, which is the name to be used in a custom operation. Bu ad, hesaplama ifadesinin açılış kıvırcık ayraç başında kapsamına girer.This name comes into scope at the start of the opening curly brace of the computation expression. Bu nedenle, bu blokta özel bir işlemle aynı ada sahip tanımlayıcılar kullanmamalısınız.Therefore, you shouldn’t use identifiers that have the same name as a custom operation in this block. Örneğin, sorgu ifadeleri gibi all last tanımlayıcıların kullanılmasından kaçının.For example, avoid the use of identifiers such as all or last in query expressions.

Yeni Özel İşlemlerle mevcut Oluşturucuları GenişletmeExtending existing Builders with new Custom Operations

Zaten bir oluşturucu sınıfınvarsa, özel işlemleri bu oluşturucu sınıfın dışından genişletilebilir.If you already have a builder class, its custom operations can be extended from outside of this builder class. Uzantıları modüller halinde bildirilmelidir.Extensions must be declared in modules. Ad alanları, aynı dosya ve türün tanımlandığı aynı ad alanı bildirim grubu dışında uzantı üyeleri içeremez.Namespaces cannot contain extension members except in the same file and the same namespace declaration group where the type is defined.

Aşağıdaki örnek, varolan Microsoft.FSharp.Linq.QueryBuilder sınıfın uzantısını gösterir.The following example shows the extension of the existing Microsoft.FSharp.Linq.QueryBuilder class.

type Microsoft.FSharp.Linq.QueryBuilder with

    [<CustomOperation("existsNot")>]
    member _.ExistsNot (source: QuerySource<'T, 'Q>, predicate) =
        Enumerable.Any (source.Source, Func<_,_>(predicate)) |> not

Ayrıca bkz.See also