How can I change this program to immediately process every line of text in case of interactive input? Preferably flush buffer every newline character.
main = do
input <- T.getContents
mapM_ T.putStrLn $ T.lines input
Update: Something is still missing. Take a look (???? is after newline, stdout is printed out after reaching EOF on stdin) :
> cat Test.hs
import System.IO
import Data.Text as T
import Data.Text.IO as T
main = do
hSetBuffering stdout LineBuffering
input <- T.getContents
mapM_ T.putStrLn $ T.lines input
> runhaskell Test.hs
a
????
a
????
> runhaskell --version
runghc 7.6.3
>
You want to use hSetBuffering from System.IO:
import System.IO
main = do
hSetBuffering stdout LineBuffering
input <- T.getContents
mapM_ T.putStrLn $ T.lines input
It seems like you want to use lazy input to interleave reading lines and handling them.
getContents from Data.Text.IO is not lazy, and will read everything before returning anything at all.
Import the version from Data.Text.Lazy.IO instead.
Related
This question already has an answer here:
Wrong IO actions order using putStr and getLine
(1 answer)
Closed 1 year ago.
I was learning haskell from Learn You a Haskell For Great Good book. There was this code
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
when i am running this code in gitbash at first it is just asking for any input after giving the input text and hitting enter( say the input text was soham) it is showing Give me some input: SOHAM.
Then i changed the code to
import Control.Monad
import Data.Char
main = forever $ do
putStrLn "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
and after running it is showing me Give me some input: and asking for an input. after giving the same input soham it is showing SOHAM
Again changing the code to
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStr $ map toUpper l
It is just taking input again and again and when i am pressing the end of file key(ctrl+C) it is showing all the output one after another side by side but the out puts are like the original code.
Why such variations are happening ?
This is likely due to buffering: with LineBuffering it will flush in whenever a new line is output. This thus means that if you use putStr, and the string itself does not contain a new line '\n' character, it will buffer the ouput and wait until a new line is written to actually write the output to the console
You can set it to NoBuffering to write the content immediately to the console. You can change the buffering settings for the stdout with hSetBuffering :: Handle -> BufferMode -> IO ():
import Control.Monad
import Data.Char
import System.IO
main = do
hSetBuffering stdout NoBuffering
forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
another option is to flush the buffer only for putStr with hFlush :: Handle -> IO (), and thus not change the buffering policy itself:
import Control.Monad
import Data.Char
import System.IO
main = do $ forever
putStr "Give me some input: "
hFlush stdout
l <- getLine
putStrLn $ map toUpper l
I'm on Ubuntu. When I run ghci on Terminal and do this:
Prelude Control.Monad System.IO> forever $ getChar >>= print
The result is something like this:
a'a'
b'b'
C'C'
%'%'
\'\\'
1'1'
''\''
"'"'
^X'\CAN'
^?'\DEL'
^CInterrupted.
That is, the characters I type in my keyboard are being flushed into output. How can I prevent this and have only print as the writer?
To prevent input being flushed into the output (or "echoed"), use hSetEcho stdin False.
Prelude> import System.IO
Prelude System.IO> import Control.Monad
Prelude System.IO Control.Monad> hSetEcho stdin False
Prelude System.IO Control.Monad> forever $ getChar >>= print
'a'
'\n'
'b'
'c'
This can be used to do things like read in a password.
My program has a bug I want to see within GHCi:
$ ./my-program < ./my-data
Prelude.foldl1: empty list
I tried changing stdin or getLine in GHCi but it doesn't seem to affect the getLine my program uses, even if I load afterwards:
$ ghci
Prelude> import System.IO
Prelude System.IO> getLine <- fmap hGetLine $ openFile "my-data" ReadMode
:l "my-program.hs"
:main
Do I need to rewrite all my IOs to take an explicit handle to be able to test them in GHCi?
You can try wrapping your program in something like this (tested, working code):
import qualified System.IO
import qualified GHC.IO.Handle
filename = "/tmp/myfilename"
main = do
h <- System.IO.openFile filename System.IO.ReadMode
old_stdin <- GHC.IO.Handle.hDuplicate System.IO.stdin
GHC.IO.Handle.hDuplicateTo h System.IO.stdin
System.IO.hClose h
realMain
GHC.IO.Handle.hDuplicateTo old_stdin System.IO.stdin
realMain = ...
It should also be possible to define a user-defined GHCi command that does this for any GHCi command, but I haven't tried yet. See here for someone's .ghci that redirects command's stdout with a user-defined :redir command.
I think you want:
ghci> :set args YOUR_ARG
ghci> main
Or
ghci> :main YOUR_ARG
See here: How to set a program's command line arguments for GHCi?
Suppose I have this code:
import qualified Data.ByteString as B
main = do
... --some code that gets an array of ByteStrings called "files"
putStrLn "Saving myFile1"
B.writeFile "myFile1.bin" $ files!!0
putStrLn "Saving myFile2"
B.writeFile "myFile2.bin" $ files!!1
putStrLn "Done!"
Instead of printing out each string before the subsequent file writing, it just seems to print out everything at once after the files have been saved. This kind of functionality is inconvenient for notifying the user of the progress, since they'll only get these messages once all the file writing is done.
My code is obviously not this bad (I'm using a list of functions with sequence) but this illustrates the problem well. Is there anything I'm doing wrong or is this there no way around this?
Like #WillemVanOnsem stated, this is a buffering issue. You can manually flush the buffer after each print or you can change the buffering mode entirely.
import System.IO
import qualified Data.ByteString as B
main = do
... --some code that gets an array of ByteStrings called "files"
putStrLn "Saving myFile1"
hFlush stdout
B.writeFile "myFile1.bin" $ files!!0
putStrLn "Saving myFile2"
hFlush stdout
B.writeFile "myFile2.bin" $ files!!1
putStrLn "Done!"
hFlush stdout
or
import System.IO
import qualified Data.ByteString as B
main = do
... --some code that gets an array of ByteStrings called "files"
hSetBuffering stdout LineBuffering
putStrLn "Saving myFile1"
B.writeFile "myFile1.bin" $ files!!0
putStrLn "Saving myFile2"
B.writeFile "myFile2.bin" $ files!!1
putStrLn "Done!"
import Data.Char
main = do
c <- getChar
if not $ isUpper c
then do putChar $ toUpper c
main
else putChar '\n'
Loading and executing in GHCi:
λ> :l foo.hs
Ok, modules loaded: Main.
λ> main
ñÑsSjJ44aAtTR
λ>
This consumes one character at time.
But in a terminal:
[~ %]> runhaskell foo.hs
utar,hkñm-Rjaer
UTAR,HKÑM-
[~ %]>
it consumes one line at time.
Why does it behave differently?
When you run a program in terminal it uses LineBuffering by default, but in ghci it is set to NoBuffering. You can read about it here. You will have to remove buffering from stdin and stdout to get the similar behavior.
import Data.Char
import System.IO
main = do
hSetBuffering stdin NoBuffering
hSetBuffering stdout NoBuffering
foo
foo = do
c <- getChar
if not $ isUpper c
then do putChar $ toUpper c
foo
else putChar '\n'