title: How to write a generic, recursive extension method in F#?
body: <p>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 Stackoverflow Community Wiki piece at <a href="https://stackoverflow.com/questions/18978523/write-an-rx-retryafter-extension-method">Write an Rx "RetryAfter" extension method</a>. It's defined like so</p>
  
  <pre class="lang-ml prettyprint-override"><code>public static IObservable&lt;TSource&gt; RetryAfterDelay&lt;TSource, TException&gt;(
      this IObservable&lt;TSource&gt; source,
      TimeSpan retryDelay,
      int retryCount,
      IScheduler scheduler) where TException : Exception
      {
          return source.Catch&lt;TSource, TException&gt;(ex =&gt;
          {
              if (retryCount &lt;= 0)
              {
                  return Observable.Throw&lt;TSource&gt;(ex);
              }
  
              return
                  source.DelaySubscription(retryDelay, scheduler)
                  .RetryAfterDelay&lt;TSource, TException&gt;(
                    retryDelay, --retryCount, scheduler);
          });
  }
  </code></pre>
  
  <p>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 <code>The field, constructor or member 'retryAfterDelay' is not defined</code></p>
  
  <pre><code>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/.
  [&lt;assembly:Extension&gt;]
  do ()
  
  [&lt;Extension&gt;]
  type ObservableExtensions =
  
      [&lt;Extension&gt;]
      static member inline retryAfterDelay((source: IObservable&lt;_&gt;), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable&lt;_&gt; =
          source.Catch(fun ex -&gt; source.DelaySubscription(retryDelay, scheduler).retryAfterDelay(retryDelay, retryCount - 1, scheduler))
  
  [&lt;EntryPoint&gt;]
  let main argv =
      0
  </code></pre>
  
  <p>Should this be possible? I've tried to come up with an example of this particular case, but thus far in vain.</p>
  
  <p>&lt;edit: Now a whole program included. Nugets are <code>Install-Package Rx-Main</code> and <code>Install-Package FSharp.Reactive</code>, compiled with VS 2013, .NET 4.5.1 and FSharp.Core 4.3.1.0 in Debug mode.</p>
  
  <p>&lt;edit 2: There's a tangential note regarding the keyword <code>rec</code> in recursive member functions at <a href="https://stackoverflow.com/questions/1996035/record-type-recursive-member-functions-and-the-rec-keyword">Record-type recursive member functions and the “rec” keyword</a>. In short, it's concluded the <code>rec</code> binding in recursive member functions is incorrect and thus compiler flags it as an error.</p>
  
  <p>&lt;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...</p>
  
  <pre><code>[&lt;Extension&gt;]
  type ObservableExtensions =   
  
      [&lt;Extension&gt;]
      static member inline retryAfterDelay((source: IObservable&lt;_&gt;), (retryDelay: TimeSpan), retryCount, (scheduler: IScheduler)): IObservable&lt;_&gt; =
          ObservableExtensions.retryAfterDelay(source.DelaySubscription(retryDelay, scheduler), retryDelay, retryCount - 1, scheduler)
  </code></pre>
  
  <p>&lt;edit 4:
  Taking cues from elsewhere and <strong>Gustavo's</strong> answer and to honor the original piece of code with the <a href="http://msdn.microsoft.com/en-us/library/dd233203.aspx" rel="nofollow">type constraints</a>, I came up with the following</p>
  
  <pre><code>//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/.
  [&lt;assembly:Extension&gt;]
  do ()
  
  [&lt;Extension&gt;]
  type ObservableExtensions =   
  
      [&lt;Extension&gt;]
      [&lt;CompiledName("PascalCase")&gt;]
      static member inline retryAfterDelay&lt;'TSource, 'TException when 'TException :&gt; System.Exception&gt;(source: IObservable&lt;'TSource&gt;, retryDelay: int -&gt; TimeSpan, maxRetries, scheduler: IScheduler): IObservable&lt;'TSource&gt; =
              let rec go(source: IObservable&lt;'TSource&gt;, retryDelay: int -&gt; TimeSpan, retries, maxRetries, scheduler: IScheduler): IObservable&lt;'TSource&gt; =
                      source.Catch&lt;'TSource, 'TException&gt;(fun ex -&gt; 
                          if maxRetries &lt;= 0 then
                              Observable.Throw&lt;'TSource&gt;(ex)
                          else
                              go(source.DelaySubscription(retryDelay(retries), scheduler), retryDelay, retries + 1, maxRetries - 1, scheduler))
              go(source, retryDelay, 1, maxRetries, scheduler)
  </code></pre>
  
  <p>A few notes</p>
  
  <ol>
  <li>I'm uncertain if <code>'TSource</code> makes any difference or would the wildcard <code>_</code> as used in previous versions be just as good. Nevertheless, I believe this represents the original code.</li>
  <li>I modified the interface to include a factory function to create a delay. The function could be, for instance, be <code>let dummyRetryStrategy(retryCount: int) = TimeSpan.FromSeconds(1.0)</code> and an example use case would be <code>let a = Seq.empty&lt;int&gt;.ToObservable().retryAfterDelay(dummyRetryStrategy, 3, Scheduler.Default)</code>.</li>
  <li>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.</li>
  <li>Would the interface be a usable one from other .NET languages such as C# and VB.NET. I have actually <a href="https://codereview.stackexchange.com/questions/48342/polling-parsing-validating-and-handling-data-cleanly-and-efficiently">a post pending on a code</a> 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).</li>
  </ol>