Это связано с ленивой оценкой.Я попытаюсь объяснить это настолько интуитивно, насколько это возможно.
Когда вы пишете interact (unlines . map (show . length) . lines)
, каждый раз, когда вводится символ, мы фактически не знаем, каким может быть следующий выходной символ, пока вынажмите Ввод.Таким образом, вы получаете ожидаемое поведение.
Однако в каждой точке в interact (unlines . map id . lines) = interact id
каждый раз, когда вы вводите символ, гарантируется, что этот символ будет включен в вывод.Таким образом, если вы вводите символ, этот символ также выводится немедленно.
Это одна из причин того, что слово «ленивый» немного неправильное.Это правда, что Haskell будет оценивать что-то только тогда, когда это необходимо, но оборотная сторона в том, что когда это нужно, он сделает это как можно скорее.Здесь Haskell нужно оценить вывод, так как вы хотите напечатать его, поэтому он оценивает его столько, сколько может - по одному символу за раз - по иронии судьбы, заставляя его казаться нетерпеливым!
В частности, interact
isn 't предназначен для пользовательского ввода в реальном времени - он предназначен для ввода файлов, когда вы передаете файл в исполняемый файл с помощью bash.Это должно быть выполнено примерно так:
$ runhaskell Interactor.hs < my_big_file.txt > list_of_lengths.txt
Если вы хотите построчную буферизацию, вам, вероятно, придется делать это вручную, если вы не хотите «обманывать» компилятор, как это делает Виллем.Вот некоторый очень простой код, который работает так, как вы ожидаете, но обратите внимание, что у него нет состояния выхода в отличие от interact
, который заканчивается в EOF.
main = do
ln <- getLine -- Buffers until you press enter
putStrLn ln -- Print the line we just got
main -- Loop forever