オペレーショナルモナドアプローチを使用して、ライター機能を備えたモナドを作成しました。それから私はそれが怠惰に機能しないことに気づきました。
以下のコードには、rogueWriter
それぞれが文字列を書き込む無限に多くのステートメントを実行するがあります。プログラムは終了しませんが、無限出力の一部の文字のみが必要です。
私の分析の後、私がからに変更するrunMyWriter rogueWriter
とrunWriter rogueWriter
、すべてがうまくいくので、不正なライターは実際には非常に友好的であることに気づきました(笑)。
質問:
- 行動を最もよく説明するにはどうすればよいですか?
- コードを機能させるにはどうすればよいですか?
- どのモナド変換
SomeMonadT
子がそれぞれ同じ問題を
SomeMonadT Writer w
引き起こしますか。WriterT w SomeMonad
(おそらくいくつかの例?)
編集:ここで無限の文字列を逆にしようとしている可能性はありますか?SjoerdVisscherのソリューションと私のソリューションの顕著な違いは
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)