既然你已经用 Haskell 标记了这个,我会在这方面回答:在 Haskell 中,相当于在Cont
monad 中进行 CPS 转换,它将一个值x
转换为一个接受一个参数并应用它的高阶函数到x
.
所以,首先,这里是常规 Haskell 中的 1 + 2:(1 + 2)
这里是 continuation monad:
contAdd x y = do x' <- x
y' <- y
return $ x' + y'
...信息量不大。为了看看发生了什么,让我们拆解 monad。首先,删除do
符号:
contAdd x y = x >>= (\x' -> y >>= (\y' -> return $ x' + y'))
该return
函数将一个值提升到 monad 中,在这种情况下实现为\x k -> k x
,或使用中缀运算符部分作为\x -> ($ x)
。
contAdd x y = x >>= (\x' -> y >>= (\y' -> ($ x' + y')))
运算符(读作“bind”)将(>>=)
monad 中的计算链接在一起,在这种情况下实现为\m f k -> m (\x -> f x k)
. 将绑定函数更改为前缀形式并替换为 lambda,以及为清楚起见进行了一些重命名:
contAdd x y = (\m1 f1 k1 -> m1 (\a1 -> f1 a1 k1)) x (\x' -> (\m2 f2 k2 -> m2 (\a2 -> f2 a2 k2)) y (\y' -> ($ x' + y')))
减少一些功能应用:
contAdd x y = (\k1 -> x (\a1 -> (\x' -> (\k2 -> y (\a2 -> (\y' -> ($ x' + y')) a2 k2))) a1 k1))
contAdd x y = (\k1 -> x (\a1 -> y (\a2 -> ($ a1 + a2) k1)))
还有一些最终的重新排列和重命名:
contAdd x y = \k -> x (\x' -> y (\y' -> k $ x' + y'))
换句话说:函数的参数已从数字更改为接受数字并返回整个表达式的最终结果的函数,正如您所期望的那样。
编辑:一位评论者指出,它contAdd
本身仍然采用咖喱风格的两个论点。这是明智的,因为它不直接使用延续,但不是必需的。否则,您需要首先在参数之间拆分函数:
contAdd x = x >>= (\x' -> return (\y -> y >>= (\y' -> return $ x' + y')))
然后像这样使用它:
foo = do f <- contAdd (return 1)
r <- f (return 2)
return r
请注意,这实际上与早期版本没有什么不同;它只是将每个部分应用程序的结果打包为延续,而不仅仅是最终结果。由于函数是一等值,所以 CPS 表达式持有数字与持有函数的表达式之间没有显着差异。
请记住,我在这里以非常冗长的方式写出东西,以明确所有步骤,其中某些东西处于延续传递风格。
附录:您可能会注意到最终表达式看起来与单子表达式的脱糖版本非常相似。这不是巧合,因为单子表达式的内嵌特性允许它们根据先前的值改变计算结构,这与延续传递风格密切相关。在这两种情况下,你在某种意义上都具体化了因果关系的概念。