Search code examples
haskelliomonadslazy-evaluationpointfree

Question about hFlush and lazy evaluation


I've got three definitions of the same function:

prompt :: String -> IO String
prompt = (getLine <*) . (hFlush stdout <*) . putStrLn

prompt' :: String -> IO String
prompt' str = do
    putStrLn str
    hFlush stdout
    getLine

prompt'' :: String -> IO String
prompt'' str = putStrLn str >> hFlush stdout >> getLine

prompt' and prompt'' both flush stdout before running getLine, but not prompt. Why is this?


Solution

  • Because that's not what you asked for. Since

    prompt = (getLine <*) . (hFlush stdout <*) . putStrLn
    

    we can just add an argument to see what we get:

    prompt str = ((getLine <*) . (hFlush stdout <*) . putStrLn) str
               = getLine <* hFlush stdout <* putStrLn str
    

    This asks to run the actions getLine, hFlush stdout, and putStrLn str, in that order. (Then the result value of that sequence of actions is whatever result value getLine had at the very beginning.) You want this instead:

    prompt str = putStrLn str *> hFlush stdout *> getLine
    

    or:

    prompt = (*> getLine) . (*> hFlush stdout) . putStrLn
    

    (In fact, the default buffering in most cases is line buffering or less, and you are calling putStrLn, not putStr, so none of these solutions actually need a call to hFlush stdout!)