계산 식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, monoids, monad 변환기 및 applicative 함수를 표현 하는 방법으로 합니다.Depending on the kind of computation expression, they can be thought of as a way to express monads, monoids, monad transformers, and applicative functors. 그러나 다른 언어와 달리 (같은 do 표기법 Haskell에서), 단일 추상화를 얽매여 있지 않는 하며 매크로 또는 다른 형태의에 의존 하지 마십시오 편리 하 고 상황에 맞는 구문을 위해 메타 프로그래밍 합니다.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!에 자체를 사용 하기 위한 syntactic sugar 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. 작성기 클래스를 설명 하는 또 다른 방법은 대부분의 작업을 사용자 지정할 수 있도록에 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! 정의한를 Bind(x, f) 작성기 형식에서 멤버 위치 f 생성을 unit입니다.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

와 마찬가지로 합니다 yield 키워드에서 C#, 반복 될 때마다 계산 식의 각 요소가 생성 될 합니다.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! 는 해당 항목 생성 백--하나씩 결과 평면화 합니다.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.

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!

부터 F# 4.5 합니다 match! 키워드를 사용 하면 인라인 해당 결과에 다른 계산 식 및 패턴 일치에 대 한 호출 합니다.Starting with F# 4.5, 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# 핵심 라이브러리는 세 개의 기본 제공 계산 식을 정의 합니다. 식 시퀀스, 비동기 워크플로, 및 쿼리 식을합니다.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 'U :> 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> 빈 호출 else 의 분기 if...then 계산 식에는 식입니다.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. 다음 하나의 구문에서 반환 되는 워크플로 개체를 전달할 수 있도록 이러한 메서드의 시그니처는 결합 하 고 서로 중첩 될 수 있도록 합니다.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 binder.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.

다음 코드 예제에는 일련의 단계는 한 번에 한 번 계산한 계산을 캡슐화 하는 계산 식을 보여 줍니다.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. 사용자 지정 작업을 정의할 때 결과 정의 해야 하 고 계산 식에서 메서드에 대 한 합니다.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. 예를 들어, 같은 식별자를 사용 하지 않도록 all 또는 last 쿼리 식에 있습니다.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