Тайм-аут и небезопасное выполнение
Я получаю некоторую практику в Haskell, исследую некоторые области, с которыми я не знаком, но я не могу понять поведение, которое я получаю, смешивая System.Timeout
а также System.IO.Unsafe
,
Я лениво читаю поток, с getContents
, фильтруя его с помощью чистой функции, и выводя результаты. Типичный фильтр будет выглядеть так:
import Data.List(break)
import System.Timeout(timeout)
import System.IO.Unsafe(unsafePerformIO)
main = do
text <- getContents
putStr $ aFilter text
aFilter text = h ++ t where
(h, t) = getUntil "\n" text
getUntil separator text = break (\x -> (separator!!0) == x) text
И с таким фильтром программа читает все stdin, как и ожидалось, и выводит в stdout. Но если я сделаю что-то вроде:
aFilter text = result where
l = consumeWithTimeout (getUntil "\n" text)
result = case l of
Nothing -> "Timeout!\n"
Just (h, t) -> h ++ t
consumeWithTimeout (x, y) = unsafePerformIO $! timeout 6 $! deepseq x (return (x, y))
Я ожидаю, что моя программа мгновенно прекратит работу, напечатайте "Timeout!" сообщение и закройте. Вместо этого он висит там, ожидая ввода.
Я ошибаюсь, думая, что timeout
функция оценивается при запуске программы? Я ожидаю, что это произойдет, потому что я немедленно записываю часть возвращаемого значения в стандартный вывод, и программное обеспечение реагирует каждый раз, когда я ввожу строку. Является unsafePerformIO
вставлять какую-то лень в мою функцию? Или это вставляет лень во внутренние органы System.Timeout
?
2 ответа
Причина в том что return $! (x, y)
не строго оценивает x
или же y
, Он только оценивает конструктор кортежа, который не обязательно оценивает его поля x
а также y
,
Итак, что происходит в вашей программе, так это return $! (x, y)
немедленно преуспевает, фактически не пытаясь оценить x
а также y
, h ++ t
часть затем начинает оценивать h
который, когда он, наконец, начинает блокировать для ввода.
Это, кстати, причина, по которой вы не должны использовать unsafePerformIO
: вы не можете легко рассуждать о том, когда эффекты действительно происходят.
Я бы ожидал
timeout 6 $! return $! (x, y)
никогда не запускать тайм-аут при нормальной рабочей нагрузке. Приведенный выше код не требует оценки x
а также y
, Может быть, используя evaluate
поможет здесь.
Далее, используя unsafePerformIO
для этой задачи выглядит довольно излишним. С помощью unsafePerformIO
должно быть сделано только в крайнем случае.