不,这种实现callcc
不依赖于惰性求值。为了证明这一点,我将用一种严格的函数式语言来实现它,并展示之后的任何内容k n
都不会被执行。
我将使用的严格的函数式语言是 JavaScript。由于 JavaScript 不是静态类型的,因此您不需要声明newtype
. 因此,我们首先在 JavaScript 中定义 monad的return
和>>=
函数。Cont
我们将分别调用这些函数unit
和bind
:
function unit(a) {
return function (k) {
return k(a);
};
}
function bind(m, k) {
return function (c) {
return m(function (a) {
return k(a)(c);
});
};
}
接下来我们定义callcc
如下:
function callcc(f) {
return function (c) {
return f(function (a) {
return function () {
return c(a);
};
})(c);
};
}
现在我们可以定义quux
如下:
var quux = callcc(function (k) {
var n = 5;
return bind(k(n), function () {
alert("Hello World!");
return unit(25);
});
});
请注意,我alert
在第二个参数中添加了一个bind
来测试它是否被执行。现在,如果您调用quux(alert)
它,它将显示5
但不会显示"Hello World!"
。这证明 to 的第二个参数bind
从未被执行。亲自查看演示。
为什么会这样?让我们从quux(alert)
. 通过 beta 减少它相当于:
(function (k) {
var n = 5;
return bind(k(n), function () {
alert("Hello World!");
return unit(25);
});
})(function (a) {
return function () {
alert(a);
};
})(alert);
通过 beta 再次减少它,它变成:
bind(function () {
alert(5);
}, function () {
alert("Hello World!");
return unit(25);
})(alert);
接下来通过 beta 减少bind
我们得到:
(function (c) {
return (function () {
alert(5);
})(function (a) {
return (function () {
alert("Hello World!");
return unit(25);
})(a)(c);
});
})(alert);
现在我们可以看到为什么"Hello World!"
从未显示。通过减少 beta,我们正在执行function () { alert(5); }
. 这个函数的工作就是调用它的参数,但它从来没有这样做过。因为这个执行停止并且"Hello World!"
永远不会显示。综上所述:
该callcc
函数不依赖于惰性求值。
由callcc
terminate after创建的函数k
被调用不是因为惰性求值,而是因为调用k
通过不调用它的第一个参数来破坏链并因此立即返回。
这让我回到你的问题:
我们可以从它的定义中k
看出\a -> cont $ \_ -> h a
,在上面我们已经\x -> runCont ((\_ -> return 25) x) c
被传入了被下划线忽略的参数。最终,它return 25
被有效地“忽略”了,因为从未使用下划线参数,因此从惰性评估中它从未被评估过。
你错了。如您所见k
,(\a -> cont $ \_ -> h a)
函数(\x -> runCont ((\_ -> return 25) x) c)
被传递到被k
. 在那之前你是对的。然而,这并不意味着return 25
因为惰性评估而没有评估。它根本没有被评估,因为该函数(\x -> runCont ((\_ -> return 25) x) c)
从未被调用。我希望能把事情弄清楚。