If you wrap the body with some (tail) recursive function in the try ... with block, then the function is no longer tail recursive, because the call frame cannot be dropped during the recursive call - it needs to be stored on the stack with the registered exception handler.
For example, let's say you have something like iter for a List :
let rec iter f list = try match list with | [] -> () | x::xs -> fx; iter f xs with e -> printfn "Failed: %s" e.Message
When you call iter f [1;2;3] , then it will create 4 nested stack frames with exception handlers (and if you add rethrow to the with branch, it will actually display an error message 4 times).
You cannot add exception handlers without breaking tail recursion. However, you usually do not need nested exception handlers. Therefore, the best solution is to rewrite the function so that it does not have to handle exceptions in all recursive calls:
let iter f list = let rec loop list = match list with | [] -> () | x::xs -> fx; loop xs try loop list with e -> printfn "Failed: %s" e.Message
This has a slightly different meaning - but it does not create nested exception handlers, and the loop can still be fully tail recursive.
Another option is to add exception handling only over the body, excluding the tail recursive call. Actually, the only thing that can throw an exception in this example is a call to f ;
let rec iter f list = match list with | [] -> () | x::xs -> try fx with e -> printfn "Failed: %s" e.Message iter f xs
Tomas petricek
source share