Вопрос о hFlush и ленивой оценке - PullRequest
2 голосов
/ 28 октября 2019

У меня есть три определения одной и той же функции:

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' и prompt'' оба сбрасывают стандартный вывод перед запуском getLine, но не prompt. Почему это?

1 Ответ

7 голосов
/ 28 октября 2019

Потому что это не то, что вы просили. Поскольку

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

, мы можем просто добавить аргумент, чтобы увидеть, что мы получаем:

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

Это просит выполнить действия getLine, hFlush stdout и putStrLn str,в этой последовательности. (Тогда результатом результата этой последовательности действий будет то значение результата, которое getLine имело в самом начале.) Вместо этого вы хотите:

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

или:

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

(Фактически, буферизацией по умолчанию в большинстве случаев является строковая буферизация или меньше, и вы звоните putStrLn, а не putStr, поэтому none этих решений на самом деле требуется вызов hFlush stdout!)

...