# 计算表达式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. 根据计算表达式的类型，可以将其视为表示 monad、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. 但与其他语言不同 (如 Haskell) 中的 notation ，它们不与单个抽象相关，也不依赖于宏或其他形式的元编程来实现方便且上下文相关的语法。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

• 非确定性计算Non-deterministic computations
• 异步计算Asynchronous computations
• Effectful 计算Effectful computations
• 生成计算Generative computations

### 语法概述Syntax overview

builder-expr { cexper }


let fetchAndDownload url =
async {

let processedData = processData data

return processedData
}


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


### 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!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
...
}


do!Bind(x, f) 生成器类型上的成员定义，其中 f 生成 unitdo! 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


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

for sq in squares do
printfn "%d" sq


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.

yieldYield(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!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


returnReturn(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 -> ...
}


## 内置计算表达式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

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

Combine M<'T> * M<'T> -> M<'T>M<'T> * M<'T> -> M<'T> or

M<unit> * M<'T> -> M<'T>

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

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


{ 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()

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


## 自定义操作Custom Operations

### 利用新的自定义操作扩展现有生成器Extending existing Builders with new Custom Operations

type Microsoft.FSharp.Linq.QueryBuilder with

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