Функция взаимодействия Haskell

Im new для Haskell и проблема с функцией interact. Это моя примерная программа:

main :: IO ()
main = interact inputLength

inputLength :: String -> String
inputLength input = show $ length input

Он компилируется, но при запуске не выводит вывод - просто печатает строку, которая передается ему, и переходит к следующей строке. Когда я передаю interact другую функцию String -> String следующим образом:

upperCase :: String -> String
upperCase input = map toUpper input

он работает нормально и печатает аргумент в верхнем регистре, как и ожидалось, - так что не так с первой функцией?

Ответ 1

Аргумент String → String заданный для interact должен принимать строку, содержащую все входные данные, и возвращать строку, содержащую все выходные данные. Причина, по которой вы видите выходные данные после нажатия клавиши enter с interact (map toUpper) заключается в том, что map toUpper действует лениво - он может начать выдавать выходные данные до того, как все входные данные map toUpper известны. Нахождение длины строки не таково - вся строка должна быть известна до того, как будет получен какой-либо вывод.

Вам нужно либо подать сигнал EOF, чтобы сказать, что вы закончили ввод ввода (в консоли это Control-D в системах Unix/Mac, я полагаю, что это Control-Z в Windows), тогда он даст вам длину. Или вы можете найти длину каждой строки, сказав так:

interact (unlines . map (show . length) . lines)

Это всегда будет ленивым в каждой строке, поэтому вы знаете, что вы можете получить один вывод после каждого ввода.

Так как работа со строками - такой распространенный шаблон, я хотел бы определить небольшую вспомогательную функцию:

eachLine :: (String -> String) -> (String -> String)
eachLine f = unlines . map f . lines

Тогда вы можете сделать:

main = interact (eachLine inputLength)