2014-10-19 4 views
6

Рассмотрим вырываясь из в противном случае не оконечное раз:перевод Схема вызова/куб.см, чтобы Haskell 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) 

Haskell эквивалент кода выше будет

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

Этот код делает не компилируются и жалуются на невозможность построить бесконечный тип в сложенном выражении acc. У меня уже есть идея, как устранить эту ошибку в таких случаях, как комбинатор Y, но такой же подход, похоже, не работает здесь. Каков правильный подход к подобной ситуации?

+6

Используйте 'foldM' вместо' foldl'. Здесь у вас есть, что 'v: acc' и' folded acc' не возвращают один и тот же тип. –

ответ

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

Спасибо за ответ. Он работает в этом случае, но, по-видимому, довольно специализирован. Есть ли общий подход для перевода кода схемы с вызовом/cc в Haskell или я должен всегда рассматривать каждый случай отдельно, просматривая сигнатуры типа? – zabolekar

1

Прежде всего, схема вызова/куб.см и Haskell callCC несколько разные вещи, как объяснено на странице undelimited continuations are not functions

Основная проблема заключается в том, что вы не можете вызов/куб.см вообще - даже в Схема. Ваш пример выхода из цикла намного лучше реализуется с использованием исключений - лучше с точки зрения эффективности (нет необходимости фиксировать продолжение, которое вы не будете использовать в любом случае), и лучше концептуально. Если задача состоит в том, чтобы прервать текущие продолжения, для этой цели есть инструменты. R7RS признал это и ввел исключения. Чтобы использовать исключения в Haskell, используйте ошибку или любую монаду. Например, в коде

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 вышли на свет. Если вас интересует более подробная информация, на следующей странице подробно рассказывается о проблемах с call/cc. An argument against call/cc