Goto в Haskell: Может ли кто-нибудь объяснить этот, казалось бы, безумный эффект использования продолжающейся монады?

Из этого потока (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 APP ()
collect n= tell [ (show n) ] 

main :: IO ()
main = do
   runAPP $ driver 4

Когда вы компилируете и запускаете этот код, на выходе получаем:

The quick brown fox ...
4

Числа от нуля до трех проглочены где-то в глубокой тьме этого примера.

Теперь, в «Хаскелле из реального мира» О'Салливан, Герцен и Стюарт заявляют

: «Укладка преобразователей монад аналогична составным функциям. . Если мы изменим порядок, в котором мы применяем функции, а затем получим другие результаты, мы не удивимся. То же самое и с преобразователями монад. " (Real World Haskell, 2008, стр. 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 (вот почему я недавно задал этот вопрос .)

Мы добавляем экземпляр, оставляя прослушивание, и передаем undefined:

instance (MonadWriter w m) => MonadWriter w (ContT r m) where
  tell = lift . tell
  listen = undefined
  pass = undefined

Добавляем эти строки, компилируем и запускаем. Все числа напечатаны.

Что произошло в предыдущем примере?

37
задан Community 23 May 2017 в 12:09
поделиться