Тайм-аут и небезопасное выполнение

Я получаю некоторую практику в 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 должно быть сделано только в крайнем случае.

Другие вопросы по тегам