using an external application

Hi, I'm solving the following problem - I need to use an external application - give it the input data and receive its output. However, when multiple calls are made, the results are not as expected. The simplified version of the problem is given below: import System.Cmd main = do System.Cmd.system "echo hello >output.txt" -- use the external application to create an output file o1 <- readFile "output.txt" System.Cmd.system "echo bye >output.txt" -- the second call to the external application o2 <- readFile "output.txt" putStr o1 -- "hello" expected, but "bye" printed return 0 Can you please give me some hint to solve this problem? I'm a beginning haskell developer and I'm still a bit confused by the IO monad. Thank you in advance. Petr

Petr Hoffmann writes:
I'm solving the following problem - I need to use an external application - give it the input data and receive its output. However, when multiple calls are made, the results are not as expected. The simplified version of the problem is given below:
System.Cmd.system "echo hello >output.txt" -- use the external
...
System.Cmd.system "echo bye >output.txt" -- the second call to the external application o2 <- readFile "output.txt" putStr o1 -- "hello" expected, but "bye" printed
Can you please give me some hint to solve this problem? I'm a beginning haskell developer and I'm still a bit confused by the IO monad.
I have the impression that your problem has nothing to do do with Haskell, you just rewrite your file, instead of appending to it. But perhaps I didn't look correctly... Jerzy Karczmarczuk

On Fri, 2 Nov 2007, Petr Hoffmann wrote:
Hi,
I'm solving the following problem - I need to use an external application - give it the input data and receive its output. However, when multiple calls are made, the results are not as expected. The simplified version of the problem is given below:
import System.Cmd main = do System.Cmd.system "echo hello >output.txt" -- use the external application to create an output file o1 <- readFile "output.txt" System.Cmd.system "echo bye >output.txt" -- the second call to the external application o2 <- readFile "output.txt" putStr o1 -- "hello" expected, but "bye" printed return 0
You fell into the trap, that 'readFile' works lazily, that is, data is only read when needed. If o1 is not completely evalutated until the second write to output.txt, it will not be read correctly from the disk. I think it was not a good choice to make the lazy version of 'readFile' the default. I think it would have been better to provide a strict 'readFile' and another 'readFileLazy' with warnings in the documentation.

On 11/2/07, Petr Hoffmann
I'm solving the following problem - I need to use an external application - give it the input data and receive its output.
Check out: The HSH library: "HSH is designed to let you mix and match shell expressions with Haskell programs. With HSH, it is possible to easily run shell commands, capture their output or provide their input, and pipe them to and from other shell commands and arbitrary Haskell functions at will." http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HSH-1.2.4 regards, Bas van Dijk

On 11/2/07, Petr Hoffmann
import System.Cmd main = do System.Cmd.system "echo hello >output.txt" -- use the external application to create an output file o1 <- readFile "output.txt" System.Cmd.system "echo bye >output.txt" -- the second call to the external application o2 <- readFile "output.txt" putStr o1 -- "hello" expected, but "bye" printed return 0
Can you please give me some hint to solve this problem? I'm a beginning haskell developer and I'm still a bit confused by the IO monad.
This looks like yet another case of the lazy-I/O goblins. The "readFile" function uses evil magic to avoid actually performing any I/O until the contents are actually used. In your case, I suspect that by the time "o1" is used -- i.e. in the "putStr" call -- the file contents have already changed, so the lazy I/O reads the new contents without complaining. The solution would be to use a version of "readFile" that works in a stricter way, by reading the file when it's told to, but I don't have an implementation handy. Stuart

On 11/2/07, Stuart Cook
The solution would be to use a version of "readFile" that works in a stricter way, by reading the file when it's told to, but I don't have an implementation handy.
I guess this does the job:
readFile' fp = do contents <- readFile fp let ret (x:xs) = x `seq` ret xs ret [] = return contents ret contents
Maybe the "x `seq`" part isn't necessary at all. -- Felipe.

On Fri, 2 Nov 2007, Felipe Lessa wrote:
On 11/2/07, Stuart Cook
wrote: The solution would be to use a version of "readFile" that works in a stricter way, by reading the file when it's told to, but I don't have an implementation handy.
I guess this does the job:
readFile' fp = do contents <- readFile fp let ret (x:xs) = x `seq` ret xs ret [] = return contents ret contents
Maybe the "x `seq`" part isn't necessary at all.
Awful. It reminds me on MatLab where the developers have implemented many automatisms which do arbitrary clever things in every corner case, but nothing consistent, because they thought programmers want it that way. Then the programmers must write wrappers in order to get functions with consistent behaviour around the fully automated functions. The "unlazying" procedure looks much like the "lazying" one, and I wonder whether it would be a good idea to eventually add "readFileStrict", "getContentStrict" and "hGetContentStrict" to the standard library.

On 11/2/07, Henning Thielemann
The "unlazying" procedure looks much like the "lazying" one, and I wonder whether it would be a good idea to eventually add "readFileStrict", "getContentStrict" and "hGetContentStrict" to the standard library.
As we're talking about getContents and readFile, how about
intercept :: Monad m => m a -> (a -> m b) -> m a intercept x g = do {r <- x; g r; return r}
readFileC :: FilePath -> IO String readFileC fp = intercept (readFile fp) (forkIO . process) where process xs = last xs `seq` return ()
Is readFileC useful in practice? I mean, does reading further concurrently makes sense in any useful context? Also, does intercept already exists in a library? Hoogle didn't help me a lot. Thanks, -- Felipe.

lemming:
On Fri, 2 Nov 2007, Felipe Lessa wrote:
On 11/2/07, Stuart Cook
wrote: The solution would be to use a version of "readFile" that works in a stricter way, by reading the file when it's told to, but I don't have an implementation handy.
I guess this does the job:
readFile' fp = do contents <- readFile fp let ret (x:xs) = x `seq` ret xs ret [] = return contents ret contents
Maybe the "x `seq`" part isn't necessary at all.
Awful. It reminds me on MatLab where the developers have implemented many automatisms which do arbitrary clever things in every corner case, but nothing consistent, because they thought programmers want it that way. Then the programmers must write wrappers in order to get functions with consistent behaviour around the fully automated functions.
The "unlazying" procedure looks much like the "lazying" one, and I wonder whether it would be a good idea to eventually add "readFileStrict", "getContentStrict" and "hGetContentStrict" to the standard library.
Yes, I have often thought System.IO.Strict should be added to the strict package on hackage, maybe in terms of bytestring.readFile >>= return . unpack, since we're being strict anyway. The above is also a rather odd implementation of readFile', which is usually implemented by forcing the last element of the list instead. -- Don

Can you please give me some hint to solve this problem? I'm a beginning haskell developer and I'm still a bit confused by the IO monad.
Other people have explained to the OP why unsafe lazy IO is breaking his code. Yet another piece of evidence, in my opinion, that unsafe-lazy-by-default is the wrong basic API. Provide that API as an option, sure. But as a default? Not IMO. Jules
participants (9)
-
Bas van Dijk
-
Bulat Ziganshin
-
Don Stewart
-
Felipe Lessa
-
Henning Thielemann
-
jerzy.karczmarczuk@info.unicaen.fr
-
Jules Bean
-
Petr Hoffmann
-
Stuart Cook