unsafeInterleaveIO
そのことについては、または怠惰なIOを使用しないでください。これはまさに、反復が対処するために作成された問題です。つまり、予測できないリソース管理を提供する遅延IOの回避です。秘訣は、リストを作成せず、使い終わるまで反復を使用してリストを常にストリーミングすることです。これを示すために、自分のライブラリの例を使用しますpipes
。
まず、以下を定義します。
import Control.Monad
import Control.Monad.Trans
import Control.Pipe
-- Demand only 'n' elements
take' :: (Monad m) => Int -> Pipe a a m ()
take' n = replicateM_ n $ do
a <- await
yield a
-- Print all incoming elements
printer :: (Show a) => Consumer a IO r
printer = forever $ do
a <- await
lift $ print a
さて、私たちのユーザーに意地悪をして、彼らが私たちのために本当に大きなリストを作成することを要求しましょう:
prompt100 :: Producer Int IO ()
prompt100 = replicateM_ 1000 $ do
lift $ putStrLn "Enter an integer: "
n <- lift readLn
yield n
それでは、実行してみましょう。
>>> runPipe $ printer <+< take' 1 <+< prompt100
Enter an integer:
3<Enter>
3
1つの整数しか要求しないため、ユーザーに1つの整数の入力を求めるだけです。
prompt100
からの出力に置き換えたい場合はgetLargeList
、次のように記述します。
yourProducer :: Producer b IO ()
yourProducer = do
xs <- lift getLargeList
mapM_ yield xs
...そして実行します:
>>> runPipe $ printer <+< take' 1 <+< yourProducer
IO
これにより、リストが遅延ストリーミングされ、安全でないハックを使用せずに、メモリ内にリストが作成されることはありません。必要な要素の数を変更するには、渡す値を変更するだけですtake'
このような他の例については、のpipes
チュートリアルをお読みくださいControl.Pipe.Tutorial
。
怠惰なIOが問題を引き起こす理由の詳細については、ここで見つけることができる主題に関するOlegの元のスライドをお読みください。彼は怠惰なIOの使用に関する問題を説明する素晴らしい仕事をしています。レイジーIOを使用せざるを得ないと感じるときはいつでも、本当に必要なのはiterateeライブラリです。