There are a couple of questions about tail-recursive function e.g. this and this but could not find anything similar to the following.
My understanding is that a tail-call optimised function should return an accumulated value in its last call without any further evaluation. It's quite easy to understand using factorial function, for example, which get optimized into loops 2. But it not always obvious to tell in other cases e.g. in the following, what is that last call? There are many of them as the function is called recursively more than once in the body.
Brian suggests a way of finding out but I am not sure how to make it tail-call optimised. I can pass the --tailcalls
flag to the compiler to do it automatically but does it always succeed?
f
and g
returns the same type.
type T = T of int * T list
let rec myfunc f (T (x,xs)) =
if (List.isEmpty xs) then f x
else
List.fold g acc (List.map (fun xxs -> myfunc f xxs) xs)
Any help to tail-call optimise the above code would be much appreciated.
As Jon already said, your function is not tail-recursive. The basic problem is that it needs to call itself recursively multiple times (once for every element in the
xs
list, which is done in the lambda function passed toList.map
).In case when you actually need to make multiple recursive calls, using the continuation passing style or i.e. imperative stack are probably the only options. The idea behind continuations is that every function will take another function (as the last argument) that should be executed when the result is available.
The following example shows normal version (on the left) and continuation based (on the right)
To write your function in a continuation passing style, you'll need to use a continuation-based
fold
function too. You can first avoid usingmap
by moving the operation done inmap
into the lambda function offold
:Becomes:
Then you can rewrite the code as follows (Note that both
f
andg
that you did not show in your question are now continuation-based functions, so they take additional argument, which represents the continuation):The
List.foldCont
function is a continuation-based version offold
and can be written as follows:Since you did not post a complete working example, I could not really test the code, but I think it should work.