How to prevent inputs being flushed into output? - haskell

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.

Related

putStr and putStrLn messing with the output [duplicate]

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

How can I test a program reading from stdin in GHCi?

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?

How to pipe output from an IO action into a process in haskell

I want to create a process and write some text from my haskell program into the process's stdin periodically (from an IO action).
The following works correctly in GHCi but don't work correctly when built and run. In GHCi everything works perfectly and the value from the IO action is fed in periodically. When built and run however, it seems to pause for arbitrarily long periods of time when writing to stdin of the process.
I've used CreateProcess (from System.Process) to create the handle and tried hPutStrLn (bufferent set to NoBuffering -- LineBuffering didnt work either).
So I'm trying the process-streaming package and pipes but can't seem to get anything to work at all.
The real question is this: How do i create a process from haskell and write to it periodically?
Minimal example that exhibits this behavior:
import System.Process
import Data.IORef
import qualified Data.Text as T -- from the text package
import qualified Data.Text.IO as TIO
import Control.Concurrent.Timer -- from the timers package
import Control.Concurrent.Suspend -- from the suspend package
main = do
(Just hin, _,_,_) <- createProcess_ "bgProcess" $
(System.Process.proc "grep" ["10"]) { std_in = CreatePipe }
ref <- newIORef 0 :: IO (IORef Int)
flip repeatedTimer (msDelay 1000) $ do
x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
hSetBuffering hin NoBuffering
TIO.hPutStrLn hin $ T.pack $ show x
Any help will be greatly appreciated.
This is a pipes Producer that emits a sequence of numbers with a second delay:
{-# language NumDecimals #-}
import Control.Concurrent
import Pipes
import qualified Data.ByteString.Char8 as Bytes
periodic :: Producer Bytes.ByteString IO ()
periodic = go 0
where
go n = do
d <- liftIO (pure (Bytes.pack (show n ++ "\n"))) -- put your IO action here
Pipes.yield d
liftIO (threadDelay 1e6)
go (succ n)
And, using process-streaming, we can feed the producer to an external process like this:
import System.Process.Streaming
main :: IO ()
main = do
executeInteractive (shell "grep 10"){ std_in = CreatePipe } (feedProducer periodic)
I used executeInteractive, which sets std_in automatically to NoBuffering.
Also, if you pipe std_out and want to process each match immediately, be sure to pass the --line-buffered option to grep (or use the stdbuf command) to ensure that matches are immediately available at the output.
What about using threadDelay, e.g.:
import Control.Monad (forever)
import Control.Concurrent (threadDelay)
...
forever $ do
x <- atomicModifyIORef' ref $ \x -> (x + 1, x)
hSetBuffering hin NoBuffering
TIO.hPutStrLn hin $ T.pack $ show x
threadDelay 1000000 -- 1 sec
Spawn this off in another thread if you need to do other work at the same time.
You can remove he need for the IORef with:
loop h x = do
hSetBuffering h NoBuffering
TIO.hPutStrLn h $ T.pack $ show x
threadDelay 1000000
loop h (x+1)
And, of course, you only need to do the hSetBuffering once - e.g. do it just before you enter the loop.

In Haskell, how to flush Data.Text every line?

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.

Why is executing getChar in a terminal different to executing it in GHCi?

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'

Resources