F# Асинхронный пусть! и возвращайся! вычислительное выражение

#.net #asynchronous #functional-programming #f# #mailboxprocessor

Вопрос:

Я начал читать материал о вычислительных выражениях, и, насколько я понимаю, в нем есть некоторые скрытые реализации, которые являются стандартными и пользовательскими.

Я предоставлю то, что я понимаю, и, пожалуйста, исправьте меня.

Например, в этом случае мы определяем пользовательскую реализацию для использования 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
    }


 

Я не могу вспомнить точно, но я читал, что если мы не предоставим ему реализацию — в него встроено какое-то значение по умолчанию. Например, какой — то рабочий процесс, который, когда он не получил ни одного, остановит весь рабочий процесс и вернет только ни одного, если он вернет что-то-код будет продолжен — > это по умолчанию или нет?

Поскольку ключевые слова, за которыми следует восклицательный знак, имеют некоторую дополнительную функциональность за кулисами, что находится внутри блока 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() процесс остановится, если он не получит ответа. О возвращении! Я действительно понятия не имею.

Ответ №1:

Нет, для методов выражения вычислений по умолчанию не существует реализаций. Если вам нужно специальное поведение 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 ())
 

Комментарии:

1. вы говорите, что нет реализации по умолчанию, тогда зачем позволять! работает внутри асинхронного{} ? без указания какого-либо особого поведения. Кроме того, я не получаю совпадения! и вернись!

2. поскольку он работает без какой-либо реализации привязки, это означает, что у него есть реализация по умолчанию.

3. let! работает внутри async{} , потому что есть явная реализация Bind в AsyncBuilder том, что поставляется с F# ( ссылка ). Без Bind закулисного, не существует поведения по умолчанию для let! .