简体   繁体   中英

How can I make a retry function tail recursive?

I have a discriminated union that is similar to the Result type used in Scott's Railway Oriented Programming . For simplicity's sake, it's slightly simplified here:

type ErrorMessage = ErrorMessage of string

type ValidationResult<'a> =
    | Success of 'a
    | Error of ErrorMessage

I have a corresponding module ValidationResult that contains functions that act on these ValidationResult s, one of them is a recursive retryable function that allows the parameter, f: unit -> 'a , to be called again (such as reading from stdin ) if the ValidationResult is Error :

module ValidationResult

    let doubleMap success error = function
         | Success x -> success x
         | Error e -> error e

    let rec retryable errorHandler f =
        let result = f ()
        let retry e =
            errorHandler e
            retryable errorHandler f
        doubleMap id retry result

But it isn't tail recursive and I would like to convert it to be so. How can I do that?

Just removing the call to doubleMap should do it:

let rec retryable errorHandler f =
    match f() with
    | Success x -> x
    | Error e ->
        errorHandler e
        retryable errorHandler f

The F# compiler compiles tail-recursive functions in two different ways.

  1. If the function is simple (calls itself directly), then it is compiled into a loop
  2. If the tail-recursion involves multiple different functions (or even function values), then the compiler uses the .tail IL instruction to do a tail-call. This is also a tail-call, but handled by the .NET runtime rather than eliminated by the F# compiler.

In your case, the retryable function is already tail-recursive, but it is the second kind. Daniel's answer makes it simple enough so that it becomes the first kind.

However, you can keep the function as you have it and it will be tail-recursive. The only thing to note is that the compiler does not generate the .tail instruction by default in Debug mode (as it messes up the call stack) and so you need to enable it explicitly (in project options, check "Generate tail calls").

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM