Из этой темы (Control.Monad.Cont fun, 2005) Томаш Зелонка представил функцию (прокомментированную в ясной и приятной форме Томасом Йегером).Томаш берет аргумент (функцию) тела callCC и возвращает его для последующего использования со следующими двумя определениями:
import Control.Monad.Cont
...
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
Они также упоминаются в Haskellwiki .Используя их, вы можете напомнить семантику goto в haskell, которая выглядит действительно круто:
import Control.Monad.Cont
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
main :: IO ()
main = (`runContT` return) $ do
(x, loopBack) <- getCC' 0
lift (print x)
when (x < 10) (loopBack (x + 1))
lift (putStrLn "finish")
Это печатает числа от 0 до 10.
Здесь возникает интересный момент.Я использовал это вместе с Writer Monad для решения определенной проблемы.Мой код выглядит следующим образом:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
import Control.Monad.Cont
import Control.Monad.Writer
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
-- a simple monad transformer stack involving MonadCont and MonadWriter
type APP= WriterT [String] (ContT () IO)
runAPP :: APP a -> IO ()
runAPP a= runContT (runWriterT a) process
where process (_,w)= do
putStrLn $ unlines w
return ()
driver :: Int -> APP ()
driver k = do
tell [ "The quick brown fox ..." ]
(x,loop) <- getCC' 0
collect x
when (x<k) $ loop (x+1)
collect :: Int -> APP ()
collect n= tell [ (show n) ]
main :: IO ()
main = do
runAPP $ driver 4
Когда вы компилируете и запускаете этот код, вывод:
The quick brown fox ...
4
Числа от нуля до трех проглатываются где-то в глубокой темнотеэтот пример.
Теперь в «реальном мире Хаскелла» состояния О'Салливана, Гоерцена и Стюарта
"Преобразование в монадные стекирования аналогично составлению функций. Если мы изменим порядок вмы применяем функции, а затем получаем разные результаты, мы не удивимся. Так же и с монадными трансформаторами. " (Real World Haskell, 2008, P. 442)
Я придумалс идеей поменять местами преобразователи выше:
--replace in the above example
type APP= ContT () (WriterT [String] IO)
...
runAPP a = do
(_,w) <- runWriterT $ runContT a (return . const ())
putStrLn $ unlines w
Однако, это не скомпилируется, потому что в Control.Monad.Cont нет определения экземпляра для MonadWriter (именно поэтому я недавно спросил thisвопрос .)
Мы добавляем экземпляр, оставляя listen и pass неопределенным:
instance (MonadWriter w m) => MonadWriter w (ContT r m) where
tell = lift . tell
listen = undefined
pass = undefined
Добавьте эти строки, скомпилируйте и запустите.Все числа напечатаны.
Что произошло в предыдущем примере?