简体   繁体   中英

F# Async let! & return! computation expression

I have started to read stuff about computation expressions and so far as I understand - it has some hidden implementations that are default and custom.

I will provide things that I understand and please correct me.

For example, in this case we define a custom implementation to use let.. So that every expression bound to the let! inside the logger block will be logged to the console.

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
    }


I cant remember precise but I have read that if we don't provide an implementation to it - it has some default built in. For example some workflow that when it has received None, it will stop the whole workflow and will return just none, if it will return Some - the code will continue -> is this default or not?

Since keywords that are followed by the exclamation mark have some extra functionality behind the scenes, what is it inside the async {} block?

Take this example.

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

I presume that the let. msg = inbox.Receive() let. msg = inbox.Receive() will stop the workflow if it receives a None. About return. I really have no idea.

No, there are no default implementations for computation expression methods. If you want want special behavior for Async<'T option> , you can add an extension method to AsyncBuilder . It looks like you want to short-circuit an Async<unit> , so you would want something like this:

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

The computation expression can resolve the overload between several Bind implementations, although you need to be careful: if the types are ambiguous, F# will choose a method implemented on the type itself (in this case, the default Bind) over an extension method.

// 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 default Bind implementation and `x` has type `int option`.
async {
    let! x = myAsyncIntOption
    return ()
}

Now, I've said what can be done, but I wouldn't recommend actually doing this. Instead I would do something more explicit:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM