Ваша Bind
операция ведет себя как обычная Bind
операция с async
, поэтому ваш код в основном является повторной реализацией (или оболочкой) над async
. Однако ваш Return
не имеет правильного типа (он должен быть 'T -> Async<'T>
), и ваш Delay
также отличается от обычного Delay
из async
. В общем, вы должны начинать с Bind
и Return
- использование Run
немного сложно, потому что Run
используется, чтобы обернуть весь блок foo { .. }
, и поэтому он не дает вам обычной хорошей компоновки.
Спецификация F # и свободная глава 12 из функционального программирования реального мира показывают обычные типы, которым вы должны следовать при реализации этих операций, поэтому я не буду повторять это здесь.
Основная проблема вашего подхода заключается в том, что вы пытаетесь повторить вычисления только в Run
, но построитель повторов, на который вы ссылаетесь, пытается повторить каждую отдельную операцию, вызываемую с помощью let!
. Ваш подход может быть достаточным, но если это так, вы можете просто реализовать функцию, которая пытается нормально запустить Async<'T>
и повторяет попытку:
let RetryRun count (work:Async<'T>) = async {
try
// Try to run the work
return! work
with e ->
// Retry if the count is larger than 0, otherwise fail
if count > 0 then return! RetryRun (count - 1) work
else return raise e }
Если вы действительно хотите реализовать построитель вычислений, который будет неявно пытаться повторить каждую асинхронную операцию, то вы можете написать что-то вроде следующего (это просто набросок, но он должен указывать вам правильное направление):
// We're working with normal Async<'T> and
// attempt to retry it until it succeeds, so
// the computation has type Async<'T>
type RetryAsyncBuilder() =
member x.ReturnFrom(comp) = comp // Just return the computation
member x.Return(v) = async { return v } // Return value inside async
member x.Delay(f) = async { return! f() } // Wrap function inside async
member x.Bind(work, f) =
async {
try
// Try to call the input workflow
let! v = work
// If it succeeds, try to do the rest of the work
return! f v
with e ->
// In case of exception, call Bind to try again
return! x.Bind(work, f) }