I'm struggling somewhat to translate a piece of C# code that defines a static, generic extension, recursive extension method to F#. The particular piece of code is Daniel Smith's * Community Wiki piece at Write an Rx "RetryAfter" extension method. It's defined like so
我正在努力翻译一段c#代码,该代码定义了一个静态的、通用的扩展、对f#的递归扩展方法。这段代码是Daniel Smith的*社区维基文章,写了一个Rx“RetryAfter”扩展方法。它的定义
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);
});
}
I'm unable to come up with a way to define the function so that I could call it inside the function. A current, simplified version I have is like so, wherein the compiler tells The field, constructor or member 'retryAfterDelay' is not defined
我无法给出定义函数的方法,这样我就可以在函数中调用它了。我有一个当前的简化版本,其中编译器告诉字段,构造函数或成员的“retryAfterDelay”没有定义。
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
Should this be possible? I've tried to come up with an example of this particular case, but thus far in vain.
这可能吗?我试着举出一个例子来说明这个例子,但到目前为止都是徒劳的。
<edit: Now a whole program included. Nugets are Install-Package Rx-Main
and Install-Package FSharp.Reactive
, compiled with VS 2013, .NET 4.5.1 and FSharp.Core 4.3.1.0 in Debug mode.
<编辑:现在包括了一个完整的程序。nuget是安装包rx-main和install-package fsharp。使用vs 2013,。net 4.5.1和fsharp编译。在调试模式下的核心4.3.1.0。< p>
<edit 2: There's a tangential note regarding the keyword rec
in recursive member functions at Record-type recursive member functions and the “rec” keyword. In short, it's concluded the rec
binding in recursive member functions is incorrect and thus compiler flags it as an error.
<编辑2:在记录类型递归成员函数和“rec”关键字的递归成员函数中,有一个关于关键字rec的切向注释。简而言之,它的结论是递归成员函数中的rec绑定是不正确的,因此编译器将其标记为错误。< p>
<edit 3: Maybe a potential way to achieve this is as follows. I haven't yet checked if this actually works, it may take some time so I'll just add this here an intermediary note...
<编辑3:可能实现这一目标的方法如下。我还没有检查这个是否有效,可能需要一些时间所以我在这里加上一个中间的注释…< p>
[<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)
<edit 4: Taking cues from elsewhere and Gustavo's answer and to honor the original piece of code with the type constraints, I came up with the following
<编辑4:从其他地方获取线索,gustavo的答案,并尊重原始代码的类型限制,我提出了如下。< p>
//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)
A few notes
一些笔记
- I'm uncertain if
'TSource
makes any difference or would the wildcard_
as used in previous versions be just as good. Nevertheless, I believe this represents the original code. - 我不确定“TSource是否会产生任何影响”,或者在以前的版本中使用的通配符是否也一样好。不过,我认为这代表了原始代码。
- I modified the interface to include a factory function to create a delay. The function could be, for instance, be
let dummyRetryStrategy(retryCount: int) = TimeSpan.FromSeconds(1.0)
and an example use case would belet a = Seq.empty<int>.ToObservable().retryAfterDelay(dummyRetryStrategy, 3, Scheduler.Default)
. -
我修改了接口,以包含工厂函数来创建延迟。例如,该函数可以是let dummyRetryStrategy(retryCount: int) = TimeSpan.FromSeconds(1.0)和一个示例用例将让a = Seq.empty
. toobservable()。retryAfterDelay(dummyRetryStrategy 3 Scheduler.Default)。 - The interface could be polished at least regarding the scheduler, and this is code is essentially untested. Hmm, maybe this should be linked back to the community wiki answer.
- 至少对于调度器来说,接口是可以擦亮的,而这是代码基本上没有经过测试。嗯,也许这应该和社区维基的答案联系起来。
- Would the interface be a usable one from other .NET languages such as C# and VB.NET. I have actually a post pending on a code very related to this at Code Review SO, so maybe it's best handled there (I'll update it tomorrow, in about twenty hours or so).
- 接口是否可以从其他。net语言(如c#和VB.NET)中使用。实际上,我在代码审查中有一个与此相关的代码,所以可能最好在那里处理(我将在明天更新它,大约20个小时左右)。
1 个解决方案
#1
3
You can use it as an extension method once you finish the Type declaration. So you can write the method like this:
在完成类型声明之后,可以将其用作扩展方法。你可以这样写:
[<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)
After that you can immediately use it. If you need it in another extension from the same class you can use it by re-opening again the Type declaration:
之后你就可以立即使用它了。如果您需要在同一类的另一个扩展中使用它,您可以通过重新打开类型声明来使用它:
type ObservableExtensions with
[<Extension>]
static member anotherExtension (x: IObservable<_>) = x.retryAfterDelay // now you can use it as an extension method
The alternative is using let
and rec
inside an internal function:
另一种方法是在内部函数中使用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)
Which I prefer for F# since the recursion is explicit in the code.
我更喜欢f#,因为在代码中,递归是显式的。
#1
3
You can use it as an extension method once you finish the Type declaration. So you can write the method like this:
在完成类型声明之后,可以将其用作扩展方法。你可以这样写:
[<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)
After that you can immediately use it. If you need it in another extension from the same class you can use it by re-opening again the Type declaration:
之后你就可以立即使用它了。如果您需要在同一类的另一个扩展中使用它,您可以通过重新打开类型声明来使用它:
type ObservableExtensions with
[<Extension>]
static member anotherExtension (x: IObservable<_>) = x.retryAfterDelay // now you can use it as an extension method
The alternative is using let
and rec
inside an internal function:
另一种方法是在内部函数中使用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)
Which I prefer for F# since the recursion is explicit in the code.
我更喜欢f#,因为在代码中,递归是显式的。