#.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!
.