我最近写了一个ETL,它工作正常。 我想提醒自己如何使用免费的monad,因此想转换我的ETL。 注意:我的目的不是写更好的ETL,而是让自己重新熟悉免费的monad。在重新学习免费monad的工作原理时,我一直跟踪这个问题的主题。
几个月前,我问了related question。有人评论说,我的递归函数可以使用连续传递样式使成为尾递归。我不知道该怎么做。
一些示例代码:
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
我要使尾部恢复原状的函数是bind
我的尝试看起来像
let rec bind2 (f: 'a -> FaceProgram<'b>) k z : FaceProgram<'b> =
match z with
|Pure x -> x |> f |> k
|Free x -> bind2 ???
我开始认为,实际上不可能使此尾部递归。类型FaceInstruction<'a>
已经包含一个延续,并且函数mapI
修改了该延续,因此现在尝试添加另一个延续k
是我现在无法处理的两个延续之一! / p>
答案 0 :(得分:2)
实际上,bind
实际上不是递归函数,因为在任何给定时间,在堆栈中对bind
的调用永远不会超过一个。
原因是因为bind
和mapI
都不调用bind
。请注意,它们如何立即退出而不深入堆栈。 bind
调用mapI
,但是mapI
根本不调用任何函数(除了Member1
或Member2
这是构造函数之外)。他们所做的是使用bind
和next
组成一个新的Free monad实例。必须将bind
声明为rec
,因为它需要自我引用才能将自身作为参数传递给mapI
。
需要将其解释为尾递归,这应该不太困难。