我最近写了一个ETL,效果很好。
我想提醒自己如何使用免费的 monad,因此想将我的 ETL 转换为这样的。
注意:我的目的不是写一个更好的 ETL,而是让自己重新熟悉免费的 monad。在重新学习自由单子如何工作时,我偏离了这个问题的主题。
所以我问了一个相关问题 https://stackoverflow.com/questions/50411886/does-using-a-free-monad-in-f-imply-a-higher-startup-time-and-limited-instructio几个月前。有人评论说我的递归函数可以使用连续传递风格进行尾递归。我不知道该怎么做。
一些示例代码:
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
这是我现在无法处理的两个延续之一!
事实上bind
实际上并不是一个递归函数,因为在堆栈中永远不会有多个调用bind
在任何给定时间。
原因是因为两者都不bind
nor mapI
call bind
。请注意它们是如何立即退出而不深入堆栈的。bind
calls mapI
but mapI
根本不调用任何函数(除了Member1
or Member2
它们是构造函数)。他们所做的是使用以下命令编写一个新的 Free monad 实例bind
and next
. bind
必须声明为rec
因为它需要一个自引用来将自身作为参数传递给mapI
.
需要将解释器实现为尾递归,这应该不会太困难。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)