3

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.

enter image description here

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

enter image description here

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.

enter image description here

Why such variations are happening ?

  • 2
    This is due to the linebuffering, use `hSetBuffering stdout NoBuffering` before the `putStrLn` (import `System.IO`). – Willem Van Onsem Dec 03 '21 at 16:07
  • 3
    Please remove your graphics as it interferes with accessibility. Cut and paste your output instead. Thank you. –  Dec 03 '21 at 16:19

1 Answers1

4

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
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • "due to buffering" means. I have a newbie in haskell. So i dont know what you are saying – Soham Chatterjee Dec 03 '21 at 16:13
  • @SohamChatterjee:: if you print to the stdout, it will usually not *immediately* write, but wait until you write a new line, and than write that entire line (or even a block of lines if you set it to `BlockBuffering`). By setting the buffering to `NoBuffering`, you write the output immediately to the console. – Willem Van Onsem Dec 03 '21 at 16:14
  • @SohamChatterjee: this in fact has not much to do with Haskell. In Java one typically also buffers writes to console to prevent spending a lot of I/O time on it. – Willem Van Onsem Dec 03 '21 at 16:17
  • Honestly i cant undestand what you are saying. I am completely new in programming stuff. This is my first language i am learning . Can you elaborate more – Soham Chatterjee Dec 03 '21 at 16:19
  • @SohamChatterjee: I think this section on flusing buffers might explain how to work with a handle that uses buffering: https://www.gnu.org/software/libc/manual/html_node/Flushing-Buffers.html – Willem Van Onsem Dec 03 '21 at 16:36
  • 2
    @SohamChatterjee Buffering can be complex for a beginner. In many, many programming languages, when you print each letter, that is not immediately sent to the screen. Instead, it is stored in a memory zone known as "the buffer". Once the buffer contains enough letters, its whole contents is sent (in jargon "flushed") to the screen, emptying the buffer. This weird mechanism is for efficiency: sending letters one by one is too slow. Flushing also happens when printing a new line: that's why `putStrLn` works fine. – chi Dec 03 '21 at 17:05
  • 1
    @SohamChatterjee `putStr` instead does not flush. With the `NoBuffering` command, you disable the buffer, forcing each letter to be printed to the screen immediately. Even if printing letters one-by-one is much slower than printing letters in a batch, for short outputs it's good enough. – chi Dec 03 '21 at 17:09