问题描述
我希望能够用F#编写一个计算表达式,如果抛出异常,它将能够重试操作.现在,我的代码如下:
让x =重试(fun()-> GetResourceX())令y =重试(fun()-> GetResourceY())令z =重试(fun()-> DoThis(x,y))等等(这显然是实际代码的抽象表示)
我需要能够重试每个功能一定次数,这些次数已在elswhere中定义.
我本以为计算表达式可以在这里帮到我,但是我看不出它如何可以帮助我删除将每个右手侧显式包装到Retryable<'T>
的方法.我可以看到计算表达式类似于:
放开!x =可重试(fun()-> GetResourceX())等等.
我了解Monad粗略地是包装器类型,但我希望能找到解决方法.我知道我可以重载运算符,并且语法非常简洁,可以将操作转换为Retryable ,但是对我而言,这只是使重复/包装更加简洁;它仍然在那里.我可以将每个函数包装为Retryable<'T>,但是再一次,我看不出在帖子顶部执行此操作的价值(对每个操作调用retry.至少是非常明确的)./p>
也许我不确定计算表达式在这里是错误的抽象.关于在这里可以做什么的任何想法?
计算表达式具有一些扩展(除了标准的monadic功能外),这为您提供了一种不错的方法.
正如您所说,单子实际上是具有一些其他行为的包装器(例如创建 Retryable<'T>
).但是,F#计算表达式还可以定义自动解包值的 Run
成员,因此 retry {return 1}
的结果只能具有类型 int 代码>.
这里是一个示例(下面的构建器):
让rnd = new System.Random()//右侧的值自动为'int'//重试指定的次数令n =重试{令n = rnd.Next(10)printfn得到%d" n如果n <5然后失败并带有!"//在某些情况下引发异常否则返回n}//您的原始示例如下所示:让x =重试{返回GetResourceX()}让y =重试{返回GetResourceY()}令z =重试{返回DoThis(x,y)}
这是 retry
构建器的定义.它并不是真正的monad,因为它没有定义 let!
(当您在另一个 retry
块中使用通过 retry
创建的计算时,它会会根据需要重试内部的X倍和外部的Y倍.
type RetryBuilder(max)=成员x.Return(a)= a//启用'return'成员x.Delay(f)= f//获取包装的正文并返回(按原样)//,以便将正文传递给运行"成员x.Zero()= failwith"Zero"//支持.. then成员x.Run(f)=//获取由延迟"创建的函数让rec loop(n)=如果n = 0,则失败,并带有失败"//重试次数已超过否则用_->尝试f().循环(n-1)最大循环让retry = RetryBuilder(4)
I want to be able to write a computation expression in F# that will be able to retry an operation if it throws an exception. Right now my code looks like:
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)
I need to be able to retry each of the functions a set number of times, which I have defined elswhere.
I was thinking a computation expression could help me here, but I don't see how it could help me remove explicitly wrapping each right hand side to a Retryable<'T>
I could see the computation expression looking something like:
let! x = Retryable( fun() -> GetResourceX())
etc.
I understand that Monads, in a crude fashion, are wrapper types, but I was hoping a way around this. I know I can overload an operator and have a very succinct syntax for converting an operation into a Retryable<'T>, but to me that's just making the repetition/wrapping more succinct; it's still there. I could wrap each function to be a Retryable<'T>, but once again, I don't see the value over doing what's done at the top of the post (calling retry on each operation. At least it's very explicit).
Maybe computation expressions are the wrong abstraction here, I'm not sure. Any ideas on what could be done here?
Computation expressions have a few extensions (in addition to the standard monadic features), that give you a nice way to do this.
As you said, the monads are essentially wrappers (creating e.g. Retryable<'T>
) that have some additional behavior. However, F# computation expression can also define Run
member which automatically unwraps the value, so the result of retry { return 1 }
can have just a type int
.
Here is an example (the builder is below):
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) }
Here is the definition of the retry
builder. It is not really a monad, because it doesn't define let!
(when you use computation created using retry
in another retry
block, it will just retry the inner one X-times and the outer one Y-times as needed).
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)
这篇关于重试计算表达式或F#中的其他构造的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!