Comments 20
>> вроде бы и фича-то хорошая
это вы про то что файл сразу не читается?
это да, прекрасно. но вот с закрытием действительно получается какая то лажа. получается, что нужно открывать файлы заранее, например в main, а дальше передавать IO String или Handle, вместо того чтоб передать просто имя файла.
это вы про то что файл сразу не читается?
это да, прекрасно. но вот с закрытием действительно получается какая то лажа. получается, что нужно открывать файлы заранее, например в main, а дальше передавать IO String или Handle, вместо того чтоб передать просто имя файла.
ну вообще можно заставить Haskell полностью считать содержимое файла до закрытия handle
вот тут описанно как это делать
stackoverflow.com/questions/296792/haskell-io-and-closing-files
ну а так в чём-то ваши размышления правильны но с другой стороны считывание файла весьма
дорогая операция в плане времени поэтому я думаю что lazy подход весьма оправдываемо правилен в плане быстро действия
вот тут описанно как это делать
stackoverflow.com/questions/296792/haskell-io-and-closing-files
ну а так в чём-то ваши размышления правильны но с другой стороны считывание файла весьма
дорогая операция в плане времени поэтому я думаю что lazy подход весьма оправдываемо правилен в плане быстро действия
1. При использовании hGetContents файл закрывать не надо, он закроется сам, когда дочитается до конца, либо когда не останется ссылок.
2. Если вам надо прочесть и закрыть, используйте withFile и строгие функции чтения данных
3. "«заставить» Haskell вычислить переменную content до закрытия файла (если кто-то знает способ, напишите)"
Пишу:
в данном случае будет достаточно, так как для вычисления длины списка, придётся этот список заполнить.
seq вычисляет первый аргумент до WHNF, т.е., грубо говоря, до конструктора, аргументы конструктора не трогает. Поэтому если вычислить seq (Just undefined) 10, результат будет просто 10, без ошибки, так как то, что внутри — не трогается.
Если надо форсировать вычисление до конца, то можно использовать deepseq/force из Control.DeepSeq: (Just undefined) `deepseq` 10 === undefined.
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.
Кончено, можно открыть файл и не париться, пускай сам закроется в конце программы. Но есть пример, где такой подход не сработает. Например, мне нужно прочитать из файла содержимое, а потом записать в ТОТ ЖЕ файл новое значение.
Такой код не работает
И никакие другие функции, типа withFile, readFile, writeFile, тут не помогут, потому что они всего лишь обертка над openFile, hGetContetns и hClose.
>Пишу:
> length content `seq` do…
За пример спасибо :)
Такой код не работает
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 — это на коленке наваять и посмотреть.
И в ByteString есть тоже две версии.
Если мы читаем бинарные данные, надо использовать ByteString, если текстовые, то встаёт вопрос кодировки, и тут нужен ByteString+Text.
String — это на коленке наваять и посмотреть.
Я так понял, все подходят к этому вопросу с практической точки зрения. Если нужен ленивый вариант, вот одна функция, если не ленивый — другая.
А меня больше задевает идеологическая сторона вопроса. Почему я пишу корректную (в энергичном смысле) программу, а она выдает некорректный результат. Вот представьте, вы бы нашли какую-нибудь лазейку, как протащить side-effect в чистую функцию. Что бы вы подумали? Я бы, например, немножко разочаровался. Я бы подумал: «ну так не интересно, я-то думал, что в haskell это невозможно».
p.s.: Я знаю что существуют функции, типа trace, которые не совсем чистые. Но, поскольку они применяются для отладочных целей, им прощается :)
А меня больше задевает идеологическая сторона вопроса. Почему я пишу корректную (в энергичном смысле) программу, а она выдает некорректный результат. Вот представьте, вы бы нашли какую-нибудь лазейку, как протащить side-effect в чистую функцию. Что бы вы подумали? Я бы, например, немножко разочаровался. Я бы подумал: «ну так не интересно, я-то думал, что в haskell это невозможно».
p.s.: Я знаю что существуют функции, типа trace, которые не совсем чистые. Но, поскольку они применяются для отладочных целей, им прощается :)
hGetContents реализована через функцию с говорящим названием unsafeInterleaveIO, потому она таит в себе опасность
Т.е. с если смотреть на идеологическую сторону вопроса — нечего использовать unsafe функции.
Т.е. с если смотреть на идеологическую сторону вопроса — нечего использовать unsafe функции.
Спасибо за пояснение.
Но давайте вспомним, для чего были придуманы монады. Разве не для того, чтобы представить программу, как некоторую чистую функцию, которая берет на вход состояние внешнего мира, и выдает на выход новое состояние?
Монады уж точно не для этого «придумывали». У вас в рассуждениях телега стоит впереди лошади — это наличие монад (с синтаксическим сахаром в виде 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
Sign up to leave a comment.
Ленивый hGetContents. Баг или фича? (Haskell)