Is is possible to convert this recursive function to tail-recursive using continuation passing style?

自作多情 提交于 2019-12-12 18:26:41

问题


I have recently written an ETL, which works just fine. I would like to remind myself how to use free monads, so would like to convert my ETL as such. Note: my intention here is not to write a better ETL, but to re-familiarize myself with free monads. In re-learing how free monads work, I got side tracked with the topic of this question.

So I asked a related question some months ago. Someone commented that my recursive function could be made tail-recursive using continuation passing style. I can't figure out how to do it.

Some sample code:

type In1 = int
type In2 = int
type Out1 = int
type Out2 = int

type FaceInstruction<'a> =
| Member1 of (In1 * (Out1 -> 'a))
| Member2 of (In2 * (Out2 -> 'a))

let private mapI f = function
    | Member1 (x, next) -> Member1 (x, next >> f)
    | Member2 (x, next) -> Member2 (x, next >> f)

type FaceProgram<'a> =
| Free of FaceInstruction<FaceProgram<'a>>
| Pure of 'a

let rec bind f = function
| Free x -> x |> mapI (bind f) |> Free
| Pure x -> f x

The function I am trying to make tail recusrive is bind

My attempts look something like

let rec bind2 (f: 'a -> FaceProgram<'b>) k  z : FaceProgram<'b> = 
    match z with
    |Pure x -> x |> f |> k
    |Free x -> bind2 ???

I am starting to think that, in fact, it is not possible to make this tail recursive. The type FaceInstruction<'a> already includes a continuation, and the function mapI modifies that continuation, so now trying to add another continuation k is one of two more continuations than I can handle right now!


回答1:


In reality bind is not actually a recursive function in the sense that in the stack there is never going to be more than one call to bind at any given time.

The reason is because neither bind nor mapI call bind. Notice how they both exit immediately without going deeper into the stack. bind calls mapI but mapI does not call any function at all (apart from Member1 or Member2 which are constructor functions). What they do is compose a new Free monad instance using bind and next. bind has to be declared as rec because it needs a self-reference to pass itself as a parameter to mapI.

It is the interpreter that needs to be implemented as tail recursive, which should not be too difficult.



来源:https://stackoverflow.com/questions/54692006/is-is-possible-to-convert-this-recursive-function-to-tail-recursive-using-contin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!