问题
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