我已经开始阅读有关计算表达式的内容,据我所知,它有一些默认和自定义的隐藏实现。
我会提供我理解的东西,请纠正我。
例如,在本例中,我们定义了一个自定义实现来使用let!。让每一个表情都绑定到let!在记录器块内部将被记录到控制台。
type LoggingBuilder() =
let log p = printfn "expression is %A" p
member this.Bind(x, f) =
log x
f x
member this.Return(x) = x
let logger = new LoggingBuilder()
let loggedWorkflow =
logger {
let! x = 42
let! y = 43
let! z = x + y
return z
}
我记不清具体是什么,但我读到过,如果我们不为它提供实现-它有一些默认的内置。例如,当它收到None时,它将停止整个工作流,只返回None,如果它返回some-代码将继续->这是默认的还是不默认的?
既然后面跟着感叹号的关键字在幕后有一些额外的功能,那么async{}块内部是什么?
举个例子。
let printerAgent =
MailboxProcessor.Start
(fun inbox ->
// the message processing function
let rec messageLoop () =
async {
// read a message
let! msg = inbox.Receive()
// process a message
printfn "message is: %s" msg
// loop to top
return! messageLoop ()
}
// start the loop
messageLoop ())
我推测,如果let! msg = inbox.Receive()
收到None,它将停止工作流。关于回归!我真的不知道。
否,计算表达式方法没有默认实现。如果您想要Async<'T option>
的特殊行为,可以向AsyncBuilder
添加一个扩展方法。看起来你想短路Async<unit>
,所以你想要这样的东西:
type FSharp.Control.AsyncBuilder with
member async.Bind(x: Async<'T option>, f: 'T -> Async<unit>) =
async.Bind(x, function
| Some x -> f x
| None -> async.Return())
计算表达式可以解决几个Bind
实现之间的重载,尽管您需要小心:如果类型不明确,F#将选择在类型本身上实现的方法(在这种情况下,是内置的Bind(,而不是扩展方法。
// Here `x` is used as an `int`, so F# knows that it needs to use
// my Bind implementation.
async {
let! x = myAsyncIntOption
let y = x + 1
return ()
}
// Here the type of `x` is unspecified, so F# chooses to use
// the built-in Bind implementation and `x` has type `int option`.
async {
let! x = myAsyncIntOption
return ()
}
现在,我已经说过可以做什么了,但我不建议实际这样做。相反,我会做一些更明确的事情:
let printerAgent =
MailboxProcessor.Start
(fun inbox ->
// the message processing function
let rec messageLoop () =
async {
// read a message
match! inbox.Receive() with
| None -> return ()
| Some msg ->
// process a message
printfn "message is: %s" msg
// loop to top
return! messageLoop ()
}
// start the loop
messageLoop ())