Pro*_*Day 3 f# computation-expression
我希望能够在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)
Run Code Online (Sandbox Code Playgroud)
我需要能够重试每个函数一定次数,我已经定义了elswhere.
我在想一个计算表达式可以帮助我,但是我没有看到它如何帮助我删除明确地将每个右侧包装到Retryable <'T>
我可以看到计算表达式看起来像:
let! x = Retryable( fun() -> GetResourceX())
etc.
Run Code Online (Sandbox Code Playgroud)
我知道莫纳德,粗糙的,是包装类型,但我希望能解决这个问题.我知道我可以重载一个操作符并且有一个非常简洁的语法将操作转换为Retryable <'T>,但对我来说这只是让重复/包装更简洁; 它还在那里.我可以换每个功能是可重试<'T>,但再一次,我看不出在做什么在后的顶部完成值(每个操作调用重试.至少这是非常明确的).
也许计算表达式在这里是错误的抽象,我不确定.关于在这里可以做些什么的任何想法?
计算表达式有一些扩展(除了标准的monadic功能),它们为你提供了一个很好的方法.
正如你所说,monad基本上是包装器(创建例如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) }
Run Code Online (Sandbox Code Playgroud)
这是retry构建器的定义.它实际上不是monad,因为它没有定义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)
Run Code Online (Sandbox Code Playgroud)