2010-05-06 2 views
10

Я пытаюсь получить более глубокое понимание лени в Haskell.Как лень и ввод-вывод работают вместе в Haskell?

я представлял себе следующий фрагмент сегодня:

data Image = Image { name :: String, pixels :: String } 

image :: String -> IO Image 
image path = Image path <$> readFile path 

Привлекательность в том, что я мог бы просто создать экземпляр изображения и передать его вокруг; если мне нужны данные изображения было бы прочитать лениво - если нет, то время и память стоимость чтения файла можно было бы избежать:

main = do 
    image <- image "file" 
    putStrLn $ length $ pixels image 

Но это то, как он на самом деле работает? Как лень совместима с IO? Будет ли readFile вызываться независимо от того, получаю ли я доступ к pixels image или будет ли время выполнения оставить этот файл неоцененным, если я не буду ссылаться на него?

Если изображение действительно прочитано лениво, то не возможно ли, что действия ввода-вывода могут произойти не в порядке? Например, что, если сразу после вызова image я удаляю файл? Теперь вызов putStrLn ничего не найдет, когда он попытается прочитать.

ответ

17

Как лень совместим с I/O?

Короткий ответ: Это не так.


Длинный ответ: IO действия строго упорядочены, для довольно многое причин вы думаете. Разумеется, любые чистые вычисления, выполненные с результатами, могут быть ленивыми; например, если вы читаете в файле, выполняете некоторую обработку и затем распечатываете некоторые результаты, вероятно, что обработка, не требуемая для вывода, не будет оцениваться. Тем не менее, весь файл будет считан, даже части, которые вы никогда не используете. Если вы хотите ленивый I/O, у вас есть примерно два вариант:

  • Ролла собственная явная отложенная загрузка процедура и такая, как вы бы в любом строгом языке. Кажется раздражающим, предоставленным, но, с другой стороны, Хаскелл делает строгий, императивный язык. Если вы хотите попробовать что-то новое и интересное, попробуйте посмотреть на Iteratees.

  • Чит, как обманывающий мошенник. Функции such as hGetContents будут делать ленивые по требованию ввода-вывода для вас, без вопросов. В чем подвох? Он (технически) нарушает ссылочную прозрачность. Чистый код может косвенно вызвать побочные эффекты, и могут произойти смешные вещи, связанные с упорядочением побочных эффектов, если ваш код действительно запутан. hGetContents и друзья реализованы using unsafeInterleaveIO, что ... точно то, что он говорит на жестяной банке. Это нигде не похоже на то, что вы взорваетесь, используя unsafePerformIO, но учтите, что вас предупреждают.

+1

Спасибо за этот ответ! На самом деле, это было описание RWH hGetContents, которое смутило меня в этом вопросе. Я не понимал, что это особый случай, и использовал небезопасные вызовы ввода-вывода внизу. Итак, в основном, мой пример читает файл, как только действие readFile обрабатывается? Это кажется намного более последовательным, если так. – Bill

+5

@Bill: Вот реализация для readFile, прямо из стандартных библиотек GHC: 'readFile name = openFile name ReadMode >> = hGetContents' Таким образом, нет, ваш пример попадает под категорию« cheating cheater ». Тем не менее, ленивые функции ввода-вывода, как правило, достаточно безопасны для повседневного практического использования, поэтому не переживайте слишком много, если чистота для вас очень важна. –

+1

Я знаю, что Олег говорит, что 'unsafeInterleaveIO' нарушает ссылочную прозрачность, но я не согласен. Я бы сказал, что это просто недетерминированно, как и многие вещи в монаде IO. «GetCurrentTime» нарушает ссылочную прозрачность, потому что я могу использовать его для определения того, какая из двух экзистенциально равных функций реализована более эффективно? –

9

Lazy I/O нарушает чистоту Haskell. Результаты от readFile действительно производятся лениво, по запросу. Порядок, в котором происходят действия ввода-вывода, не фиксирован, поэтому да, они могут произойти «не в порядке». Проблема удаления файла перед вытягиванием пикселей реальна. Короче говоря, ленивый ввод-вывод - отличное удобство, но это инструмент с очень острыми краями.

Книга о реальном мире Haskell имеет lengthy treatment of lazy I/O и проходит через некоторые подводные камни.

 Смежные вопросы

  • Нет связанных вопросов^_^