Writer, реализованный с помощью Operational Monad, не работает лениво

Я написал монаду с функцией Writer, используя подход Operational Monad. Затем я заметил, что она не работает лениво.

В приведенном ниже коде есть rogueWriter, который выполняет бесконечно много операторов, каждый из которых записывает строку. Программа не завершается, хотя требуется только несколько символов бесконечного вывода.

После анализа я заметил, что rogue writer на самом деле довольно дружелюбен (хаха), потому что когда я перехожу от runMyWriter rogueWriter к runWriter rogueWriter, все идет хорошо.

Вопросы:

  1. Как лучше объяснить такое поведение?
  2. Как я должен изменить свой код, чтобы это работало?
  3. Какие преобразования монад SomeMonadT вызывают ту же проблему в
    SomeMonadT Writer w resp. WriterT w SomeMonad. (возможно, есть примеры?)

Edit: Возможно ли, что я пытаюсь обратить бесконечную строку? Разительная разница между решением Sjoerd Visscher и моим

w `mappend` ws  resp.  ws `mappend` w

Код:

{-# LANGUAGE GADTs, FlexibleContexts, TypeSynonymInstances,
                    FlexibleInstances, MultiParamTypeClasses #-}

module Writer where

import Control.Monad.Identity
import Control.Monad.Operational
import Control.Monad.Writer
import Data.Monoid

data MyWriterI w a where
    Tell :: w -> MyWriterI w ()

type MyWriterT w = ProgramT (MyWriterI w)

type MyWriter w = (MyWriterT w) Identity

runMyWriterT :: (Monad m, Monoid w) => MyWriterT w m a -> m (a, w)
runMyWriterT prog = run prog mempty
  where
    run prog ws = viewT prog >>= flip eval ws
    eval (Return a)       ws = return (a, ws)
    eval (Tell w :>>= is) ws = run (is ()) (ws `mappend` w)

runMyWriter :: (Monoid w) => MyWriter w a -> (a, w)
runMyWriter prog = runIdentity (runMyWriterT prog)

instance (Monad m, Monoid w) => MonadWriter w (MyWriterT w m) where
    tell   = singleton . Tell
    listen = undefined
    pass   = undefined

-- Demonstration of the problem:

rogueWriter :: MonadWriter String m => m ()
rogueWriter = mapM_ (tell . show) [1..]

main = let (_, infiniteOutput) = runMyWriter rogueWriter
       in putStrLn (take 20 infiniteOutput)
10
задан Duschvorhang 26 December 2011 в 13:47
поделиться