定点组合器的定义是一个函数F,它接受一个函数f并返回一个函数p使得
给定F(f) = p 然后p = f(p)
可以编写许多可能的定点组合器。不要让直截了当让您认为某事不是定点组合器;这是JavaScript中的一个标准定义,很简单:
var fix = function(f) {
return function(x) {
return f(fix(f))(x)
}
};
一种用法可能是计算阶乘的不动点,使用:
var fact = function(f) {
return function(n) { return (n == 0) ? 1 : (n * f(n - 1)) }
};
alert(fix(fact)(7)); // alerts us with 5040.
有关不同定点组合器(Y 组合器)的示例,请参阅this helpful blog post。
让我们看看您的 until 组合器是否计算定点。由于您使用的是单子函数,因此定点定义会稍作更改以处理单子结构,其中F 是一个(单子)定点组合器
给定F(f) = p 然后p = f* . p
其中f* . p 表示函数p 与函数f 的Kleisli 组合(在您的代码中,您将编写此kleisli(p, f),您可以将* 视为bind)。我将使用这种表示法,因为它比编写 JavaScript 更短。
然后让我们展开until 的定义,看看我们得到了什么:
until(f) = (until(f))* . f
= (until(f)* . f)* . f
= ((... . f)* . f)* . f
= ... . f* . f* . f (associativity of bind for a monad: (g* . f)* = g* . f*)
= p
p = f* . p 是吗?
... . f* . f* . f =?= f* . ... . f* . f* . f
是的——我相信是的。虽然我不认为这是一个有用的固定点。 (恐怕我还没有很好的论据——但我认为这基本上是一个最大固定点,只会发散)。
在我看来,until 中 kleisli 的参数似乎应该被交换了。也就是说,我们希望在fix 示例中做应用程序的Kleisli 等效,因此我们需要将递归调用until(f) 的一元结果传递给f:
var until = function(f) {
return function(a) {
return kleisli(until(f), f)(a);
};
};
让我们展开until 的这个新定义:
until(f) = f* . until(f)
= f* . (f* . until(f))
= f* . f* . ...
= p
p = f* . p 是吗?是的:
f* . f* ... = f* . (f* . f* . ...)
因为将 f* 的一个组合添加到 f* 组合的无限链上是相同的功能。
使用您的 kleisli 函数时,我遇到了一些分歧问题(一些评估发生得太快,因此计算会一直运行,直到我用完堆栈空间)。相反,以下似乎对我有用:
var until = function(f) {
return function(a) {
return bind(f,until(f)(a));
};
};
有关单子代码的定点的更多信息,您可以查看the work of Erkök and Launchbury。