コンピュテーション式Computation Expressions

のF#コンピュテーション式は、制御フローの構造とバインディングを使用して、シーケンス処理および結合できる計算を作成するための便利な構文を提供します。Computation expressions in F# provide a convenient syntax for writing computations that can be sequenced and combined using control flow constructs and bindings. 計算式の種類によっては、monads、モノ id、monads トランスフォーマー、およびアプリケーションを表現する方法と考えることができます。Depending on the kind of computation expression, they can be thought of as a way to express monads, monoids, monad transformers, and applicative functors. ただし、Haskellのような他の言語とは異なり、それらは1つの抽象化に関連付けられておらず、マクロやその他の形式のメタプログラミングに依存せず、便利で状況依存の構文を実現できません。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.

の概要Overview

計算には多くの形式が必要です。Computations can take many forms. 最も一般的な計算形式は、簡単に理解して変更できるシングルスレッド実行です。The most common form of computation is single-threaded execution, which is easy to understand and modify. ただし、すべての形式の計算は、シングルスレッド実行と単純なものではありません。However, not all forms of computation are as straightforward as single-threaded execution. たとえば、次のものがあります。Some examples include:

  • 非決定的な計算Non-deterministic computations
  • 非同期計算Asynchronous computations
  • Effectful の計算Effectful computations
  • 注釈の計算Generative computations

一般に、アプリケーションの特定の部分で実行する必要がある状況依存の計算があります。More generally, there are context-sensitive computations that you must perform in certain parts of an application. コンテキストに依存するコードを記述することは困難な場合があります。これは、特定のコンテキストの外では、抽象化を行わずに "リーク" の計算を簡単に行うことができるためです。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. 多くの場合、これらの抽象化は自分で記述することF#が困難です。そのため、コンピュテーション式と呼ばれる汎用的な方法があります。These abstractions are often challenging to write by yourself, which is why F# has a generalized way to do so called computation expressions.

コンピュテーション式は、文脈に依存した計算をエンコードするための統一された構文と抽象化モデルを提供します。Computation expressions offer a uniform syntax and abstraction model for encoding context-sensitive computations.

すべての計算式は、ビルダー型によってサポートされます。Every computation expression is backed by a builder type. ビルダーの型は、コンピュテーション式で使用できる操作を定義します。The builder type defines the operations that are available for the computation expression. コンピュテーション式の新しい型の作成」を参照してください。これは、カスタム計算式の作成方法を示しています。See Creating a New Type of Computation Expression, which shows how to create a custom computation expression.

構文の概要Syntax overview

すべての計算式の形式は次のとおりです。All computation expressions have the following form:

builder-expr { cexper }

ここで builder-expr はコンピュテーション式を定義するビルダー型の名前、cexper はコンピュテーション式の式の本体です。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. たとえば、async コンピュテーション式のコードは次のようになります。For example, async computation expression code can look like this:

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

        let processedData = processData data

        return processedData
    }

前の例で示したように、コンピュテーション式内で使用できる特殊な追加の構文があります。There is a special, additional syntax available within a computation expression, as shown in the previous example. コンピュテーション式では、次の式の形式を使用できます。The following expression forms are possible with computation expressions:

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

これらのキーワードおよびその他の標準F#のキーワードは、バッキングビルダーの型で定義されている場合にのみ、コンピュテーション式で使用できます。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. 唯一の例外は match!です。これは、let! を使用するための構文砂糖であり、その後に結果のパターンマッチが続きます。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.

ビルダーの型は、コンピュテーション式のフラグメントの結合方法を制御する特殊なメソッドを定義するオブジェクトです。つまり、そのメソッドは、コンピュテーション式の動作を制御します。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. ビルダークラスを記述するもう1つの方法は、ループやバインドなど、さまざまF#な構造の操作をカスタマイズできるということです。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!

let! キーワードは、別のコンピュテーション式への呼び出しの結果を名前にバインドします。The let! keyword binds the result of a call to another computation expression to a name:

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

letでコンピュテーション式への呼び出しをバインドすると、コンピュテーション式の結果は得られません。If you bind the call to a computation expression with let, you will not get the result of the computation expression. 代わりに、そのコンピュテーション式に対して、実現されていない呼び出しの値をバインドします。Instead, you will have bound the value of the unrealized call to that computation expression. let! を使用して、結果にバインドします。Use let! to bind to the result.

let! は、ビルダー型の Bind(x, f) メンバーによって定義されます。let! is defined by the Bind(x, f) member on the builder type.

do!

do! キーワードは、unitに似た型 (ビルダーの Zero メンバーによって定義) を返すコンピュテーション式を呼び出すためのものです。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<unit>です。For the async workflow, this type is Async<unit>. その他の計算式では、型が CExpType<unit>される可能性があります。For other computation expressions, the type is likely to be CExpType<unit>.

do! は、funitを生成するビルダー型の Bind(x, f) メンバーによって定義されます。do! is defined by the Bind(x, f) member on the builder type, where f produces a unit.

yield

yield キーワードは、IEnumerable<T>として使用できるように、コンピュテーション式から値を返すためのものです。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

ほとんどの場合、呼び出し元は省略できます。In most cases, it can be omitted by callers. yield を省略する最も一般的な方法は、-> 演算子を使用することです。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

多くの異なる値を生成する可能性がある複雑な式の場合、場合によってはキーワードを省略するだけで、次のことが可能です。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#で yield キーワードを使用する場合と同様に、コンピュテーション式の各要素は反復処理されるときに返されます。As with the yield keyword in C#, each element in the computation expression is yielded back as it is iterated.

yield は、ビルダー型の Yield(x) メンバーによって定義されます。 x は、返される項目です。yield is defined by the Yield(x) member on the builder type, where x is the item to yield back.

yield!

yield! キーワードは、コンピュテーション式から値のコレクションをフラット化するためのものです。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

評価された場合、yield! によって呼び出されるコンピュテーション式の項目は1つずつ返され、結果がフラット化されます。When evaluated, the computation expression called by yield! will have its items yielded back one-by-one, flattening the result.

yield! は、ビルダー型の YieldFrom(x) メンバーによって定義されます。 x は値のコレクションです。yield! is defined by the YieldFrom(x) member on the builder type, where x is a collection of values.

yieldとは異なり、yield! は明示的に指定する必要があります。Unlike yield, yield! must be explicitly specified. 計算式では、その動作は暗黙的ではありません。Its behavior isn't implicit in computation expressions.

return

return キーワードは、コンピュテーション式に対応する型の値をラップします。The return keyword wraps a value in the type corresponding to the computation expression. yieldを使用した計算式とは別に、計算式を "完了" するために使用されます。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

return は、ビルダーの型の Return(x) メンバーによって定義されます。 x は、ラップする項目です。return is defined by the Return(x) member on the builder type, where x is the item to wrap.

return!

return! キーワードは、コンピュテーション式の値を認識し、その結果をコンピュテーション式に対応する型にラップします。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! は、ビルダーの型の ReturnFrom(x) メンバーによって定義されます。 x は別のコンピュテーション式です。return! is defined by the ReturnFrom(x) member on the builder type, where x is another computation expression.

match!

match! キーワードを使用すると、別のコンピュテーション式への呼び出しをインライン化し、結果にパターン一致を行うことができます。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 -> ...
    }

match!でコンピュテーション式を呼び出すと、let!のような呼び出しの結果が認識されます。When calling a computation expression with match!, it will realize the result of the call like let!. これは、通常、結果が省略可能であるコンピュテーション式を呼び出すときに使用されます。This is often used when calling a computation expression where the result is an optional.

組み込みのコンピュテーション式Built-in computation expressions

コアF#ライブラリでは、シーケンス式非同期ワークフロークエリ式という3つの組み込みのコンピュテーション式が定義されています。The F# core library defines three built-in computation expressions: Sequence Expressions, Asynchronous Workflows, and Query Expressions.

新しい種類のコンピュテーション式の作成Creating a New Type of Computation Expression

ビルダークラスを作成し、クラスに特定の特別なメソッドを定義することで、独自のコンピュテーション式の特性を定義できます。You can define the characteristics of your own computation expressions by creating a builder class and defining certain special methods on the class. ビルダークラスでは、次の表に示すように、必要に応じてメソッドを定義できます。The builder class can optionally define the methods as listed in the following table.

次の表では、ワークフロービルダークラスで使用できるメソッドについて説明します。The following table describes methods that can be used in a workflow builder class.

メソッドMethod 一般的な署名Typical signature(s) 説明Description
Bind M<'T> * ('T -> M<'U>) -> M<'U> コンピュテーション式で let! および do! に対して呼び出されます。Called for let! and do! in computation expressions.
Delay (unit -> M<'T>) -> M<'T> 計算式を関数としてラップします。Wraps a computation expression as a function.
Return 'T -> M<'T> コンピュテーション式の return に対して呼び出されます。Called for return in computation expressions.
ReturnFrom M<'T> -> M<'T> コンピュテーション式の return! に対して呼び出されます。Called for return! in computation expressions.
Run M<'T> -> M<'T>M<'T> -> M<'T> or

M<'T> -> 'T
コンピュテーション式を実行します。Executes a computation expression.
Combine M<'T> * M<'T> -> M<'T>M<'T> * M<'T> -> M<'T> or

M<unit> * M<'T> -> M<'T>
コンピュテーション式でシーケンス処理を行うために呼び出されます。Called for sequencing in computation expressions.
For seq<'T> * ('T -> M<'U>) -> M<'U>seq<'T> * ('T -> M<'U>) -> M<'U> or

seq<'T> * ('T -> M<'U>) -> seq<M<'U>>
コンピュテーション式の for...do 式に対して呼び出されます。Called for for...do expressions in computation expressions.
TryFinally M<'T> * (unit -> unit) -> M<'T> コンピュテーション式の try...finally 式に対して呼び出されます。Called for try...finally expressions in computation expressions.
TryWith M<'T> * (exn -> M<'T>) -> M<'T> コンピュテーション式の try...with 式に対して呼び出されます。Called for try...with expressions in computation expressions.
Using 'T * ('T -> M<'U>) -> M<'U> when 'T :> IDisposable コンピュテーション式の use バインドに対して呼び出されます。Called for use bindings in computation expressions.
While (unit -> bool) * M<'T> -> M<'T> コンピュテーション式の while...do 式に対して呼び出されます。Called for while...do expressions in computation expressions.
Yield 'T -> M<'T> コンピュテーション式の yield 式に対して呼び出されます。Called for yield expressions in computation expressions.
YieldFrom M<'T> -> M<'T> コンピュテーション式の yield! 式に対して呼び出されます。Called for yield! expressions in computation expressions.
Zero unit -> M<'T> コンピュテーション式の if...then 式の空の else 分岐に対して呼び出されます。Called for empty else branches of if...then expressions in computation expressions.
Quote Quotations.Expr<'T> -> Quotations.Expr<'T> コンピュテーション式が Run メンバーに引用符として渡されることを示します。Indicates that the computation expression is passed to the Run member as a quotation. 計算のすべてのインスタンスを引用符に変換します。It translates all instances of a computation into a quotation.

ビルダークラスのメソッドの多くは、M<'T> コンストラクトを使用して返します。通常、これは、結合する計算の種類を特徴とする個別に定義された型です。たとえば、非同期ワークフローの場合は Async<'T>、シーケンスワークフローの場合は Seq<'T> です。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. これらのメソッドのシグネチャを使用すると、1つのコンストラクトから返されるワークフローオブジェクトを次の構造体に渡すことができるように、それらを組み合わせて入れ子にすることができます。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. コンパイラは、コンピュテーション式を解析するときに、前の表のメソッドとコンピュテーション式のコードを使用して、一連の入れ子になった関数呼び出しに式を変換します。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.

入れ子になった式の形式は次のとおりです。The nested expression is of the following form:

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

上記のコードでは、コンピュテーション式ビルダークラスで定義されていない場合、RunDelay への呼び出しは省略されます。In the above code, the calls to Run and Delay are omitted if they are not defined in the computation expression builder class. ここでは {| cexpr |}として示されているコンピュテーション式の本体は、次の表に示す翻訳によって、ビルダークラスのメソッドを含む呼び出しに変換されます。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. コンピュテーション式 {| cexpr |} は、これらの変換に従って、expr がF#式で、cexpr がコンピュテーション式である場合に、再帰的に定義されます。The computation expression {| cexpr |} is defined recursively according to these translations where expr is an F# expression and cexpr is a computation expression.

[式]Expression 変換Translation
{ 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()

前の表では、other-expr テーブルに記載されていない式について説明しています。In the previous table, other-expr describes an expression that is not otherwise listed in the table. ビルダークラスは、すべてのメソッドを実装する必要はなく、前の表に一覧表示されているすべての変換をサポートします。A builder class does not need to implement all of the methods and support all of the translations listed in the previous table. 実装されていない構造体は、その型のコンピュテーション式では使用できません。Those constructs that are not implemented are not available in computation expressions of that type. たとえば、コンピュテーション式で use キーワードをサポートしない場合は、ビルダークラスの Use の定義を省略できます。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.

次のコード例は、一度に1ステップずつ評価できる一連のステップとして計算をカプセル化するコンピュテーション式を示しています。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. 判別共用体型 OkOrExceptionは、これまでに評価された式のエラー状態をエンコードします。A discriminated union type, OkOrException, encodes the error state of the expression as evaluated so far. このコードは、いくつかのビルダーメソッドの定型的な実装など、計算式で使用できる一般的なパターンを示しています。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

コンピュテーション式には基になる型があり、この式はを返します。A computation expression has an underlying type, which the expression returns. 基になる型は、計算された結果または実行可能な遅延計算を表すことができます。また、一部の型のコレクションを反復処理する方法を提供する場合もあります。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. 前の例では、基になる型は最終的にでした。In the previous example, the underlying type was Eventually. シーケンス式の場合、基になる型は System.Collections.Generic.IEnumerable<T>です。For a sequence expression, the underlying type is System.Collections.Generic.IEnumerable<T>. クエリ式の場合、基になる型は System.Linq.IQueryableです。For a query expression, the underlying type is System.Linq.IQueryable. 非同期ワークフローの場合、基になる型はAsyncです。For an asynchronous workflow, the underlying type is Async. Async オブジェクトは、結果を計算するために実行する作業を表します。The Async object represents the work to be performed to compute the result. たとえば、 Async.RunSynchronouslyを呼び出して計算を実行し、その結果を返します。For example, you call Async.RunSynchronously to execute a computation and return the result.

カスタム操作Custom Operations

コンピュテーション式でカスタム操作を定義し、コンピュテーション式で演算子としてカスタム操作を使用することができます。You can define a custom operation on a computation expression and use a custom operation as an operator in a computation expression. たとえば、クエリ式にクエリ演算子を含めることができます。For example, you can include a query operator in a query expression. カスタム操作を定義する場合は、コンピュテーション式の Yield メソッドと For メソッドを定義する必要があります。When you define a custom operation, you must define the Yield and For methods in the computation expression. カスタム操作を定義するには、それをコンピュテーション式のビルダークラスに配置し、 CustomOperationAttributeを適用します。To define a custom operation, put it in a builder class for the computation expression, and then apply the CustomOperationAttribute. この属性は、引数として文字列を受け取ります。これはカスタム操作で使用される名前です。This attribute takes a string as an argument, which is the name to be used in a custom operation. この名前は、コンピュテーション式の左中かっこの先頭にあるスコープに含まれます。This name comes into scope at the start of the opening curly brace of the computation expression. したがって、このブロック内のカスタム操作と同じ名前を持つ識別子は使用しないでください。Therefore, you shouldn’t use identifiers that have the same name as a custom operation in this block. たとえば、クエリ式で alllast などの識別子を使用しないようにします。For example, avoid the use of identifiers such as all or last in query expressions.

新しいカスタム操作を使用した既存のビルダーの拡張Extending existing Builders with new Custom Operations

既にビルダークラスがある場合は、そのカスタム操作をこのビルダークラスの外部から拡張できます。If you already have a builder class, its custom operations can be extended from outside of this builder class. 拡張機能はモジュール内で宣言する必要があります。Extensions must be declared in modules. 名前空間には、同じファイルと、型が定義されている同じ名前空間宣言グループ以外の拡張メンバーを含めることはできません。Namespaces cannot contain extension members except in the same file and the same namespace declaration group where the type is defined.

次の例は、既存の Microsoft.FSharp.Linq.QueryBuilder クラスの拡張を示しています。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

関連項目See also