2014-10-19 83 views
6

讓我們考慮打破了原本的摺疊非終止:平移方案的呼叫/ cc到哈斯克爾callCC

(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) 

哈斯克爾相當於上面的代碼將是

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

此代碼沒有編譯和抱怨無法在摺疊表達式中構造無限類型。我已經知道如何在像Y組合器這樣的情況下消除這種錯誤,但是同樣的方法在這裏似乎不起作用。這種情況的正確方法是什麼?

+6

使用'foldM'而不是'foldl'。在這裏你有'v:acc'和'fold acc'不會返回相同的類型。 – 2014-10-19 04:49:47

回答

6

是的;作爲j。 abrahamson說,

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 

的作品。

+1

感謝您的回答。它適用於這種情況,但似乎相當專業。是否有一種將Call代碼翻譯成Haskell的一般方法,還是我應該總是通過查看類型簽名來分別考慮每個案例? – zabolekar 2014-10-19 13:11:04

1

首先,方案的呼叫/ CC和Haskell callCC有些不同的東西,如在頁面上說明 undelimited continuations are not functions

的主要問題是,你可能並不需要呼叫/ CC在所有 - 即使是在方案。使用異常更好地實現你的循環的例子 - 從效率的角度來看更好(不需要捕獲你不會使用的延續),並且在概念上更好。如果任務是放棄當前的延續,那麼就有用於此目的的工具。 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 

話務員呼叫/ cc的引入計劃時,有與控制操作符的經驗。現在我們有很多經驗,並且call/cc的許多嚴重缺陷已經暴露。如果您對更多細節感興趣,下面的頁面會詳細討論call/cc的問題。 An argument against call/cc