Рассмотрим следующую программу на Haskell. Я пытаюсь программировать в «потоковом стиле», где функции работают с потоками (реализованы здесь просто в виде списков). Такие вещи, как normalStreamFunc, отлично работают с ленивыми списками. Я могу передать бесконечный список в normalStreamFunc и эффективно получить другой бесконечный список, но с функцией, сопоставленной с каждым значением. Такие вещи, как effectfulStreamFunc, работают не так хорошо. Действие ввода-вывода означает, что мне нужно оценить весь список, прежде чем я смогу извлечь отдельные значения. Например, вывод программы такой:
a
b
c
d
"[\"a\",\"b\"]"
но мне нужен способ написать effectfulStreamFunc, чтобы программа выдала следующее:
a
b
"[\"a\",\"b\"]"
оставляя оставшиеся действия без оценки. Я могу представить решение, использующее unsafePerformIO, но, скажем, я убираю это со стола. Вот программа:
import IO
normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs
effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = do
putStrLn x
rest <- effectfulStreamFunc xs
return (reverse(x):rest)
main :: IO ()
main = do
let fns = ["a", "b", "c", "d"]
es <- effectfulStreamFunc fns
print $ show $ take 2 es
Обновление:
Спасибо всем за полезные и вдумчивые отзывы. Я раньше не видел оператора sequence
, о котором полезно знать. Я думал о (менее элегантном) способе передачи значений IO (String) вместо Strings, но для стиля программирования, который имеет ограниченную полезность, так как я хочу, чтобы другие функции потока действовали на сами строки, а не на действия, которые могут произвести строку. Но, исходя из обдумывания других ответов, я думаю, что понимаю, почему это вообще неразрешимо. В простом случае, который я представил, я действительно хотел оператор sequence
, поскольку я думал, что упорядочение потока подразумевает упорядочение действий. На самом деле такой порядок не подразумевается. Это становится более понятным для меня, когда я думаю о функции потока, которая принимает два потока в качестве входных данных (например, попарно добавление двух потоков). Если оба «входящих» потока выполнили IO, порядок этих действий IO не определен (если, конечно, мы не определяем его, упорядочивая его в монаде IO). Проблема решена, спасибо всем!