Подтвердить что ты не робот

Что делает fixIO?

Документы System.IO содержат загадочную недокументированную функцию fixIO. Свой источник только добавляет тайну:

fixIO :: (a -> IO a) -> IO a
fixIO k = do
    m <- newEmptyMVar
    ans <- unsafeInterleaveIO (takeMVar m)
    result <- k ans
    putMVar m result
    return result

Это похоже на моральный эквивалент разыменования NULL (чтение с пустого MVar). Действительно, попробуйте:

import System.IO
main = fixIO $ \x -> putStrLn x >> return x

приводит к ошибке "поток, заблокированный бесконечно в операции MVar"

В поиске ничего не появляется, чтобы сохранить 15-летнее сообщение от самого Саймона Пейтона-Джонса, в котором он предоставляет вышеупомянутый источник, и надеется, что это придаст смысл ясно (и все же здесь я).

Может кто-то пролить свет на это? Что делает fixIO и когда я должен его использовать?

4b9b3361

Ответ 1

fixIO является эквивалентом IO fix.

Вероятно, вы видели это определение последовательности фибоначчи:

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

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

Мы можем использовать fix, чтобы сделать то же самое без необходимости определять переменную привязывая узел для нас:

fix $ \fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)

Это удобно, если вам не нужно полностью соблюдать последовательность фибоначчи, вы просто хотите знать его десятый элемент:

λ (fix $ \fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)) !! 9
55

fixIO аналогичен, за исключением того, что он позволяет вам рекурсивно выполнять выходные операции ввода-вывода. Вот почему у вас возникла ваша "заблокированная потоком" ошибка - вы использовали исходный результат без его определения.

λ fmap (take 10) . fixIO $ \fibs -> putStrLn "computing fibs" >> return (1 : 1 : zipWith (+) fibs (tail fibs))
computing fibs
[1,1,2,3,5,8,13,21,34,55]