I was developing a web site using haskell programs as cgi's, and I found a strange behavior that I would like to know whether it is normal. I have reduced the "problem" to the next program: fEntrada = "fich.txt" fSalida = "fich.txt" creaFich :: IO() creaFich = writeFile fEntrada "me molo" main :: IO () main = do x <- readFile fEntrada -- print x -- In the second try, uncomment this line writeFile fSalida "" writeFile fSalida x Running the next commands (suposing that $ is the prompt of a linux shell and main> is the prompt of hugs) main> creaFich main> main $ cat fich.txt will give us different results if we comment or uncomment the second line of the main body, although the meaning of the program is the same. I understand that this is caused by the lazyness, that doesn't evaluate the expression "x <- readFile fEntrada" until it's necessary, but.. is it normal that we have to think about this "problem" when programming? In my case, this behavior caused my program to fail (and it was really complicated to find why) and the only solution I found was indeed printing to a scratch file the string I was reading inmediatly after reading it (really, after telling hugs to read it). I find this a great disadvantage as oppose of the imperative paradigm, overall because is like having to control something (near to concurrence if you ask me) that has not been asked for! I 'll be thankful of any comments or replies you send to me. Greetings, Diego y tal <deigote@gmail.com>
Your problem is, as you pointed out, that readFile does lazy IO. Although the semantics of it can be a bit confusing at times, it is useful for applications where you have a large file which is being consumed, and you don't want to allocate all of the memory for it before doing any processing. Laziness lets you read the file as needed -- you may not even need it all, depending on what is being done. This is quite helpful when you have something like a couple gigabytes of data on disk which needs processing. It can however be confusing at first that it may not finish reading the file before the file is altered, or, in situations involving handles, before the handle is closed. You can write a strict IO version of readFile in Haskell as follows: import IO hGetContents' hdl = do e <- hIsEOF hdl if e then return [] else do c <- hGetChar hdl cs <- hGetContents' hdl return (c:cs) readFile' fn = do hdl <- openFile fn ReadMode xs <- hGetContents' hdl hClose hdl return xs If you use readFile', it will ensure that the entire file is read and memory for the string is allocated before continuing. This ought to solve your problem. (Aside: I think this sort of thing should get included in the libraries, if for no other reason than that this issue comes up from time to time, and it would be handy to not have to write extra code to do strict IO.) hope this helps, - Cale On 28/07/05, Diego y tal <deigote@tiscali.es> wrote:
I was developing a web site using haskell programs as cgi's, and I found a strange behavior that I would like to know whether it is normal. I have reduced the "problem" to the next program:
fEntrada = "fich.txt" fSalida = "fich.txt"
creaFich :: IO() creaFich = writeFile fEntrada "me molo"
main :: IO () main = do x <- readFile fEntrada -- print x -- In the second try, uncomment this line writeFile fSalida "" writeFile fSalida x
Running the next commands (suposing that $ is the prompt of a linux shell and main> is the prompt of hugs)
main> creaFich main> main $ cat fich.txt
will give us different results if we comment or uncomment the second line of the main body, although the meaning of the program is the same. I understand that this is caused by the lazyness, that doesn't evaluate the expression "x <- readFile fEntrada" until it's necessary, but.. is it normal that we have to think about this "problem" when programming? In my case, this behavior caused my program to fail (and it was really complicated to find why) and the only solution I found was indeed printing to a scratch file the string I was reading inmediatly after reading it (really, after telling hugs to read it). I find this a great disadvantage as oppose of the imperative paradigm, overall because is like having to control something (near to concurrence if you ask me) that has not been asked for! I 'll be thankful of any comments or replies you send to me.
Greetings, Diego y tal <deigote@gmail.com>
Am Donnerstag, 28. Juli 2005 20:01 schrieb Diego y tal:
I was developing a web site using haskell programs as cgi's, and I found a strange behavior that I would like to know whether it is normal. I have reduced the "problem" to the next program:
fEntrada = "fich.txt" fSalida = "fich.txt"
creaFich :: IO() creaFich = writeFile fEntrada "me molo"
main :: IO () main = do x <- readFile fEntrada -- print x -- In the second try, uncomment this line writeFile fSalida "" writeFile fSalida x
Running the next commands (suposing that $ is the prompt of a linux shell and main> is the prompt of hugs)
main> creaFich main> main $ cat fich.txt
will give us different results if we comment or uncomment the second line of the main body, although the meaning of the program is the same.
In my opinion, the program should signal an error if the print x line is commented out since then the read handle isn't closed but semi-closed at the point of the first writeFile, and therefore write access shouldn't be allowed (see library documentation of System.IO). In fact, GHC and GHCi behave this way.
[...]
Regards, Wolfgang
On Thu, Jul 28, 2005 at 08:01:17PM +0200, Diego y tal wrote:
I understand that this is caused by the lazyness,
No, it is caused by mixing laziness with side-effects, which happens when you use getContents/readFile.
that doesn't evaluate the expression "x <- readFile fEntrada" until it's necessary,
"x <- readFile fEntrada" is not an expression.
but.. is it normal that we have to think about this "problem" when programming?
You just have to know, which functions mix laziness and side-effects by using unsafeInterleaveIO (readFile, getContents, hGetContents).
I find this a great disadvantage as oppose of the imperative paradigm,
I wouldn't say this is a great disadvantage of Haskell, because: a) the imperative paradigm is available in Haskell b) readFile is a bit of hack, because it uses laziness where the order of execution matters (it uses unsafeInterleaveIO). It is generally known that readFile should be used only for the simplest I/O tasks. c) there are other ways to complete the task (however, some of them are non-standard) Best regards Tomasz
Am Montag, 1. August 2005 22:38 schrieb Tomasz Zielonka:
On Thu, Jul 28, 2005 at 08:01:17PM +0200, Diego y tal wrote: [...]
but.. is it normal that we have to think about this "problem" when programming?
You just have to know, which functions mix laziness and side-effects by using unsafeInterleaveIO (readFile, getContents, hGetContents).
Well, it is important to note that implementations of readFile etc. should use locking according to the System.IO documentation. If they do so, evaluation order may have an impact on the question whether opening a file fails or not but it changing the order of evaluation cannot change the data read from or written to a file anymore. In Diego's example, removing print x wouldn't result in different data written to the file but in a failure at the first writeFile as I already pointed out. Now, if we implement readFile so that the same file cannot be opened for writing by the same program anymore then changing the evaluation order can no longer change the effect of the program. This would be similar to Clean's I/O library where there are the sf* file reading functions where sfopen has exactly this effect of forbidding opening the same file for writing until the end of the program.
[...]
Best regards, Wolfgang
participants (4)
-
Cale Gibbard -
Diego y tal -
Tomasz Zielonka -
Wolfgang Jeltsch