По мотивам поста
mr_aleph(
reinventing CPS - functional pearl from my own soul) решил попробовать написать это на Haskell, если я, конечно, правильно понял задание :)
Для начала определим два типа:
data Continuation r = Continue (IO (Continuation r)) | Fail String | Done r
type Continuable a = forall r . ContT (Continuation r) IO a
Вычисление может либо вернуть ошибку, либо остановиться на «контрольной точке» (например, файл скачан). Соответственно если дальнейшие действия провалятся, вычисление продолжит работу с этой контрольной точки. Done здесь нужен для возврата окончательного значения.
Теперь две функции, одна прекращает вычисление с ошибкой, а вторая соответственно «коммитит» значение и продолжение:
done v = ContT $ \k -> return (Continue (k v))
stop s = ContT $ \_ -> return (Fail s)
Остаётся написать ещё две примитивные функции - одна для оборачивания ContT в наш тип, а вторая для запуска вычисления, они тоже простые:
routine :: ContT (Continuation r) IO r -> IO (Continuation r)
routine m = runContT m (return . Done)
type ContinuationState r = Either r (String, IO (Continuation r))
run :: IO (Continuation r) -> IO (ContinuationState r)
run k = do
k' <- k
case k' of
(Done v) -> return $ Left v
(Fail s) -> return $ Right (s, k)
(Continue k) -> run k
Ну и последнее, перезапуск вычисления. По сути тот же run, но принимающий промежуточный результат:
retry :: (String -> IO ()) -> ContinuationState r -> IO (ContinuationState r)
retry _ (Left v) = return $ Left v
retry out (Right (s, k)) = out s >> run k
Тест:
getInput prompt = do
liftIO $ putStrLn prompt
s <- liftIO $ getLine
if null s
then stop "Empty input"
else done s
bar = routine $ do
x <- getInput "Input value 1:"
y <- getInput "Input value 2:"
z <- getInput "Input value 3:"
return (x ++ " - " ++ y ++ " - " ++ z)
Теперь если на второй запрос ввести пустую строку, вычисление остановится, после retry продолжит сразу со второго запроса.
Ну и forkIO туда впихнуть по желанию.