# 计算表达式Computation Expressions

## 概述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


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! 由生成器类型上的 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!

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


## 创建新类型的计算表达式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>>

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.

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


ExpressionExpression 转换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()

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