Ответ 1
Не используйте unsafeInterleaveIO
или любой ленивый 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
Он запрашивает только одно целое число, так как мы требуем только одно целое!
Если вы хотите заменить 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 вызывает проблемы, прочитайте оригинальные слайды Олега по этому вопросу, которые вы можете найти здесь здесь. Он отлично справляется с объяснением проблем с использованием ленивого ввода-вывода. Каждый раз, когда вы вынуждены использовать ленивый IO, вам действительно нужна библиотека iteratee.