我希望能够在 F# 中编写一个计算表达式,以便在抛出异常时能够重试操作。现在我的代码如下所示:
let x = retry (fun() -> GetResourceX())
let y = retry (fun() -> GetResourceY())
let z = retry (fun() -> DoThis(x, y))
etc. (this is obviously an astract representation of the actual code)
我需要能够将每个函数重试一定次数,这是我在其他地方定义的。
我认为计算表达式可以在这里帮助我,但我不知道它如何帮助我删除将每个右侧显式包装到 Retryable
我可以看到计算表达式看起来像这样:
let! x = Retryable( fun() -> GetResourceX())
etc.
我知道 Monad 从粗略的角度来说是包装类型,但我希望有一种解决方法。我知道我可以重载一个运算符,并有一个非常简洁的语法来将操作转换为 Retryable,但对我来说,这只是使重复/包装更加简洁;它还在那里。我可以将每个函数包装为 Retryable,但我再一次看不到执行帖子顶部所做操作的价值(对每个操作调用重试。至少它非常明确)。
我不确定,计算表达式在这里可能是错误的抽象。关于这里可以做什么有什么想法吗?
计算表达式有一些扩展(除了标准的一元功能之外),这为您提供了一种很好的方法来做到这一点。
正如你所说,单子本质上是包装器(创建例如Retryable<'T>
)有一些额外的行为。然而,F#计算表达式也可以定义Run
自动解包该值的成员,因此结果retry { return 1 }
可以只有一个类型int
.
这是一个示例(构建器如下):
let rnd = new System.Random()
// The right-hand side evaluates to 'int' and automatically
// retries the specified number of times
let n = retry {
let n = rnd.Next(10)
printfn "got %d" n
if n < 5 then failwith "!" // Throw exception in some cases
else return n }
// Your original examples would look like this:
let x = retry { return GetResourceX() }
let y = retry { return GetResourceY() }
let z = retry { return DoThis(x, y) }
这是的定义retry
建设者。它并不是真正的单子,因为它没有定义let!
(当您使用使用创建的计算时retry
在另一个retry
块,它只会根据需要重试内部一次 X 次和外部一次 Y 次)。
type RetryBuilder(max) =
member x.Return(a) = a // Enable 'return'
member x.Delay(f) = f // Gets wrapped body and returns it (as it is)
// so that the body is passed to 'Run'
member x.Zero() = failwith "Zero" // Support if .. then
member x.Run(f) = // Gets function created by 'Delay'
let rec loop(n) =
if n = 0 then failwith "Failed" // Number of retries exceeded
else try f() with _ -> loop(n-1)
loop max
let retry = RetryBuilder(4)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)