Как стать автором
Обновить

Комментарии 20

>> вроде бы и фича-то хорошая

это вы про то что файл сразу не читается?
это да, прекрасно. но вот с закрытием действительно получается какая то лажа. получается, что нужно открывать файлы заранее, например в main, а дальше передавать IO String или Handle, вместо того чтоб передать просто имя файла.
ну вообще можно заставить Haskell полностью считать содержимое файла до закрытия handle

вот тут описанно как это делать

stackoverflow.com/questions/296792/haskell-io-and-closing-files

ну а так в чём-то ваши размышления правильны но с другой стороны считывание файла весьма
дорогая операция в плане времени поэтому я думаю что lazy подход весьма оправдываемо правилен в плане быстро действия
1. При использовании hGetContents файл закрывать не надо, он закроется сам, когда дочитается до конца, либо когда не останется ссылок.
2. Если вам надо прочесть и закрыть, используйте withFile и строгие функции чтения данных
3. "«заставить» Haskell вычислить переменную content до закрытия файла (если кто-то знает способ, напишите)"
Пишу:

length content `seq` do ...

в данном случае будет достаточно, так как для вычисления длины списка, придётся этот список заполнить.

seq вычисляет первый аргумент до WHNF, т.е., грубо говоря, до конструктора, аргументы конструктора не трогает. Поэтому если вычислить seq (Just undefined) 10, результат будет просто 10, без ошибки, так как то, что внутри — не трогается.

Если надо форсировать вычисление до конца, то можно использовать deepseq/force из Control.DeepSeq: (Just undefined) `deepseq` 10 === undefined.
Кончено, можно открыть файл и не париться, пускай сам закроется в конце программы. Но есть пример, где такой подход не сработает. Например, мне нужно прочитать из файла содержимое, а потом записать в ТОТ ЖЕ файл новое значение.
Такой код не работает
import System.IO
main = do 
    c <- withFile "1.txt" ReadMode $ hGetContents
    withFile "1.txt" WriteMode $ \h -> hPutStr h $ "<" ++ c ++ ">" 

И никакие другие функции, типа withFile, readFile, writeFile, тут не помогут, потому что они всего лишь обертка над openFile, hGetContetns и hClose.

>Пишу:
> length content `seq` do…
За пример спасибо :)
Работать со строками через String не совсем принято, надо использовать text, а там есть как строгий readFile, так и ленивый.
И в ByteString есть тоже две версии.

Если мы читаем бинарные данные, надо использовать ByteString, если текстовые, то встаёт вопрос кодировки, и тут нужен ByteString+Text.

String — это на коленке наваять и посмотреть.
НЛО прилетело и опубликовало эту надпись здесь
Я так понял, все подходят к этому вопросу с практической точки зрения. Если нужен ленивый вариант, вот одна функция, если не ленивый — другая.
А меня больше задевает идеологическая сторона вопроса. Почему я пишу корректную (в энергичном смысле) программу, а она выдает некорректный результат. Вот представьте, вы бы нашли какую-нибудь лазейку, как протащить side-effect в чистую функцию. Что бы вы подумали? Я бы, например, немножко разочаровался. Я бы подумал: «ну так не интересно, я-то думал, что в haskell это невозможно».

p.s.: Я знаю что существуют функции, типа trace, которые не совсем чистые. Но, поскольку они применяются для отладочных целей, им прощается :)
НЛО прилетело и опубликовало эту надпись здесь
hGetContents реализована через функцию с говорящим названием unsafeInterleaveIO, потому она таит в себе опасность
Т.е. с если смотреть на идеологическую сторону вопроса — нечего использовать unsafe функции.
Странно, что нет стандартной safe функции, и приходится придумывать какие-то костыли.
System.IO написан был очень давно и входит в стандарт. base же распилена на куски, чтобы не держать зоопарк, а text и bytestring уже стандарт де факто, но вносить их прямо в base не стали.
Спасибо за пояснение
Спасибо за пояснение.
Но давайте вспомним, для чего были придуманы монады. Разве не для того, чтобы представить программу, как некоторую чистую функцию, которая берет на вход состояние внешнего мира, и выдает на выход новое состояние?

Монады уж точно не для этого «придумывали». У вас в рассуждениях телега стоит впереди лошади — это наличие монад (с синтаксическим сахаром в виде do-нотации) позволяет представлять программу в виде функции, которая выглядит императивно. А не наоборот.

Это значит, что мы можем отложить вычисление «на потом», и результат от этого не изменится

Нет. С такой логикой вот такие две функции совершенно одинаковы:
foo = do 
    putStrLn "Hello, "
    putStrLn "World"
bar = do 
    putStrLn "World"
    putStrLn "Hello, "

что, очевидно, не верно.

То есть, вы ошибаетесь в том, что подразумеваете «неважность» порядка записи вызовов функций всегда. Это верно для чистых функций, но ошибочно, если пытаться использовать его внутри do-нотации.
Монады уж точно не для этого «придумывали». У вас в рассуждениях телега стоит впереди лошади — это наличие монад (с синтаксическим сахаром в виде do-нотации) позволяет представлять программу в виде функции, которая выглядит императивно. А не наоборот.

Не понял, в чем я был не прав. По-моему я написал то же самое.

То есть, вы ошибаетесь в том, что подразумеваете «неважность» порядка записи вызовов функций всегда

Я подразумеваю «неважность» порядка редукции. Это значит, что для одной и той же функции ленивое и энергичное вычисление должно давать один и тот же результат.
Так оно и редуцируется до одного и того же. Вы свою функцию main запишите без do-нотации, и сразу увидите, что «на вход» функции read разное состояние передаётся в разных примерах. Оттого и разное поведение.

Не редукция виновата, у вас просто разные функции написаны, с разным поведением.
Давайте рассмотрим ОДНУ функцию и вычислим её ДВУМЯ разными способами. Объясните мне, почему они дают разный результат?
main = do 
    file <- openFile "1.txt" ReadMode
    content <- hGetContents file
    hClose file
    print content

Давайте. Запишите её без do-нотации для начала.
main = openFile "1.txt" ReadMode >>= \file -> 
	hGetContents file >>= \content -> 
	hClose file >> 
	print content

Собственно, вот тут и видно, что в print content передаётся монада IO в которой файл уже закрыт, а с содержимым файла — никто не работал, соответственно, и читать из него не надо.

При этом, не важно, как именно её редуцировать, итоговая последовательность действий будет одинакова.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории