我正在努力翻译一段C#代码,它定义了一个静态的,通用的扩展,递归扩展方法到F#。特定的代码是Daniel Smith在Write an Rx "RetryAfter" extension method的Stackoverflow社区维基文章。它的定义如此
public static IObservable<TSource> RetryAfterDelay<TSource, TException>(
this IObservable<TSource> source,
TimeSpan retryDelay,
int retryCount,
IScheduler scheduler) where TException : Exception
{
return source.Catch<TSource, TException>(ex =>
{
if (retryCount <= 0)
{
return Observable.Throw<TSource>(ex);
}
return
source.DelaySubscription(retryDelay, scheduler)
.RetryAfterDelay<TSource, TException>(
retryDelay, --retryCount, scheduler);
});
}
我无法想出一种方法来定义函数,以便我可以在函数内部调用它。我当前的简化版本是这样的,其中编译器告诉The field, constructor or member 'retryAfterDelay' is not defined
open System
open FSharp.Reactive
open System.Reactive
open System.Reactive.Concurrency
open System.Reactive.Linq
open System.Reactive.Threading.Tasks
open System.Runtime.CompilerServices
//Note that to declare .NET compatible extensions methods "correctly" in F#, one
//needs to also add the assembly level extension attribute. There's a good summary
//by Lincoln Atkinson at http://latkin.org/blog/2014/04/30/f-extension-methods-in-roslyn/.
[<assembly:Extension>]
do ()
[<Extension>]
type ObservableExtensions =
[<Extension>]
static member inline retryAfterDelay((source: IObservable<_>), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable<_> =
source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler).retryAfterDelay(retryDelay, retryCount - 1, scheduler))
[<EntryPoint>]
let main argv =
0
这可能吗?我试图想出一个这个特例的例子,但到目前为止都是徒劳的。
&lt;编辑:现在包含了整个程序。 Nugets为Install-Package Rx-Main
和Install-Package FSharp.Reactive
,在调试模式下使用VS 2013,.NET 4.5.1和FSharp.Core 4.3.1.0进行编译。
&lt;编辑2:在Record-type recursive member functions and the “rec” keyword的递归成员函数中有关于关键字rec
的切线注释。简而言之,它总结了递归成员函数中的rec
绑定是不正确的,因此编译器将其标记为错误。
&lt;编辑3:可能实现这一目标的潜在方法如下。我还没有检查这是否真的有效,可能需要一些时间,所以我只是在这里添加一个中间注释...
[<Extension>]
type ObservableExtensions =
[<Extension>]
static member inline retryAfterDelay((source: IObservable<_>), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable<_> =
ObservableExtensions.retryAfterDelay(source.DelaySubscription(retryDelay, scheduler), retryDelay, retryCount - 1, scheduler)
&lt;编辑4: 从其他地方获取线索并 Gustavo 回答并使用type constraints兑现原始代码段,我想出了以下内容
//Note that to declare .NET compatible extensions methods "correctly" in F#, one
//needs to also add the assembly level extension attribute. There's a good summary
//by Lincoln Atkinson at http://latkin.org/blog/2014/04/30/f-extension-methods-in-roslyn/.
[<assembly:Extension>]
do ()
[<Extension>]
type ObservableExtensions =
[<Extension>]
[<CompiledName("PascalCase")>]
static member inline retryAfterDelay<'TSource, 'TException when 'TException :> System.Exception>(source: IObservable<'TSource>, retryDelay: int -> TimeSpan, maxRetries, scheduler: IScheduler): IObservable<'TSource> =
let rec go(source: IObservable<'TSource>, retryDelay: int -> TimeSpan, retries, maxRetries, scheduler: IScheduler): IObservable<'TSource> =
source.Catch<'TSource, 'TException>(fun ex ->
if maxRetries <= 0 then
Observable.Throw<'TSource>(ex)
else
go(source.DelaySubscription(retryDelay(retries), scheduler), retryDelay, retries + 1, maxRetries - 1, scheduler))
go(source, retryDelay, 1, maxRetries, scheduler)
一些注意事项
'TSource
是否有任何区别,或者先前版本中使用的通配符_
是否同样出色。不过,我相信这代表了原始代码。let dummyRetryStrategy(retryCount: int) = TimeSpan.FromSeconds(1.0)
,示例用例可以是let a = Seq.empty<int>.ToObservable().retryAfterDelay(dummyRetryStrategy, 3, Scheduler.Default)
。答案 0 :(得分:3)
完成Type声明后,您可以将它用作扩展方法。所以你可以写这样的方法:
[<Extension>]
type ObservableExtensions =
[<Extension>]
static member retryAfterDelay(source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
ObservableExtensions.retryAfterDelay(source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler)),retryDelay, retryCount - 1, scheduler)
之后你可以立即使用它。如果你需要在同一个类的另一个扩展中,你可以通过再次重新打开Type声明来使用它:
type ObservableExtensions with
[<Extension>]
static member anotherExtension (x: IObservable<_>) = x.retryAfterDelay // now you can use it as an extension method
另一种方法是在内部函数中使用let
和rec
:
[<Extension>]
static member retryAfterDelay(source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
let rec go (source: IObservable<_>, retryDelay: TimeSpan, retryCount, scheduler: IScheduler): IObservable<_> =
go (source.Catch(fun ex -> source.DelaySubscription(retryDelay, scheduler)),retryDelay, retryCount - 1, scheduler)
go (source, retryDelay, retryCount, scheduler)
我更喜欢F#,因为递归在代码中是显式的。