F# 异步让! & return!计算表达式

F# Async let! & return! computation expression

我已经开始阅读有关计算表达式的资料,据我所知 - 它有一些默认和自定义的隐藏实现。

我会提供我理解的东西,请指正。

例如,在本例中,我们定义了一个自定义实现来使用 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 时,它会停止整个工作流程,将 return 只是 none,如果它 return 一些 - 代码将继续 -> 这是否是默认设置?

由于感叹号后面的关键字在幕后有一些额外的功能,那么在 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 时停止工作流。关于return!我真的不知道。

不,计算表达式方法没有默认实现。如果你想要 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# 将选择在类型本身上实现的方法(在这种情况下,内置绑定) 通过扩展方法。

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