10

オペレーショナルモナドアプローチを使用して、ライター機能を備えたモナドを作成しました。それから私はそれが怠惰に機能しないことに気づきました。

以下のコードには、rogueWriterそれぞれが文字列を書き込む無限に多くのステートメントを実行するがあります。プログラムは終了しませんが、無限出力の一部の文字のみが必要です。

私の分析の後、私がからに変更するrunMyWriter rogueWriterrunWriter rogueWriter、すべてがうまくいくので、不正なライターは実際には非常に友好的であることに気づきました(笑)。

質問:

  1. 行動を最もよく説明するにはどうすればよいですか?
  2. コードを機能させるにはどうすればよいですか?
  3. どのモナド変換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)
4

1 に答える 1

6

あなたのプログラムが失敗する理由は正確にはわかりませんが、これが私がそれを書く方法です:

runMyWriterT prog = run prog
  where
    run prog = viewT prog >>= eval
    eval (Return a)       = return (a, mempty)
    eval (Tell w :>>= is) = do
      ~(r, ws) <- run (is ())
      return (r, w `mappend` ws)
于 2011-12-26T12:29:33.310 に答える