【问题标题】:translating Scheme call/cc to Haskell callCC将 Scheme call/cc 翻译成 Haskell callCC
【发布时间】:2014-10-19 01:38:44
【问题描述】:

让我们考虑打破原本不会终止的折叠:

(call/cc (lambda (folded)
  (stream-fold
    (lambda (acc v)
      (if (< v 5)
        (cons v acc)
        (folded acc)))
    '()
    (in-naturals 0))))
; returns '(4 3 2 1 0)

上述代码的 Haskell 等效项是

callCC $ \folded -> foldl (\acc v -> if v < 5 then v:acc else folded acc) [] [0..]

此代码无法编译并抱怨无法在表达式 folded acc 中构造无限类型。我已经知道如何在 Y 组合器之类的情况下消除这种错误,但同样的方法在这里似乎不起作用。这种情况的正确方法是什么?

【问题讨论】:

  • 使用foldM 而不是foldl。在这里你有 v:accfolded acc 不返回相同的类型。

标签: haskell scheme continuations


【解决方案1】:

是的;作为 j。亚伯拉罕森说,

import Control.Monad.Trans.Cont
import Control.Monad

bar :: Cont r [Int]
bar = callCC $ \folded ->
    foldM (\acc v -> do
        when (v >= 5) $ folded acc
        return $ v : acc) [] [0..]

thing = runCont bar id

有效。

【讨论】:

  • 感谢您的回答。它在这种情况下有效,但似乎相当专业。是否有将带有 call/cc 的 Scheme 代码转换为 Haskell 的通用方法,还是我应该始终通过查看类型签名来单独考虑每种情况?
【解决方案2】:

首先,Scheme call/cc 和 Haskell callCC 是有些不同的东西,解释 在页面上 undelimited continuations are not functions

主要问题是您可能根本不需要 call/cc —— 即使在 Scheme 中也是如此。您打破循环的示例使用异常可以更好地实现 - 从效率的角度来看更好(无需捕获您无论如何都不会使用的延续),并且在概念上更好。如果任务是中止当前的延续,则有专门用于该目的的工具。 R7RS 已经识别它并引入了异常。要在 Haskell 中使用异常,请使用 Error 或 Either monad。例如,在您的代码中

baz :: Either [Int] [Int]
baz = foldM (\acc v -> do
        when (v >= 5) $ Left acc
        return $ v : acc) [] [0..]

thing1 = either id id baz

操作符 call/cc 是在对控制操作符缺乏经验的时候被引入到 Scheme 中的。现在我们有了很多经验,call/cc 的许多严重缺点都暴露出来了。如果您对更多细节感兴趣,以下页面将详细讨论 call/cc 的问题。 An argument against call/cc

【讨论】:

    猜你喜欢
    • 2011-02-16
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2011-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多