Принудительная оценка через IORef: rnf, deepSeq или что-то еще? - PullRequest
4 голосов
/ 12 июля 2011

У меня длительный процесс, который forkIO 'd, который производит значения цвета пикселей:

takesAgesToRun :: [[Color]]

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         writeIORef ref colors

(где Color содержит три значения Double).

Как и ожидалось, при чтении на «другой стороне» IORef сохраненное значение является просто thunk, и, следовательно, блокирует основной процесс.

Я знаю, что мне нужно полностью оценить значение [[Color]] для заголовка нормальной формы, но, похоже, есть два способа добиться этого, и, кроме того, я не уверен, как включить любой из них в мой код.

Как мне поступить? Использую ли я rnf, deepSeq или какую-то другую стратегию потоков? Является ли один из них предпочтительным, а другие - устаревшими? И как это вписывается в мой код?

(PS, пожалуйста, игнорируйте тот факт, что хранить изображение в виде списка из списка цветов глупо - это просто упрощенная версия кода).

1 Ответ

5 голосов
/ 12 июля 2011

Используйте deepSeq.Он используется так же, как seq.Вы бы включили это следующим образом:

myForkedProcess :: IORef [[Color]] -> IO ()
myForkedProcess ref = do let colors = takesAgesToRun
                         deepSeq colors $ writeIORef ref colors

Это заставит «цвета» полностью оцениваться перед вызовом «writeIORef».

Для того, чтобы это работало, вам понадобитсяNFData экземпляр для Color.Как именно это написать, зависит от определения цвета, но вот два примера:

-- just for reference
data Color = Color Double Double Double

instance NFData Color where
    rnf (Color r g b) = r `seq` g `seq` b `seq` ()

-- closer to the likely actual implementation for Color
data Color2 = Color2 !Double !Double !Double

instance NFData  Color2 where
-- the default implementation is fine

Для экземпляра Color необходимо убедиться, что все компоненты цвета полностью оценены [1]всякий раз, когда цвет.Это то, что делают seq.Мы можем использовать seq вместо deepSeq здесь, потому что мы знаем, что каждый компонент является Double, поэтому полностью вычисляется с помощью seq.Если бы компонент был более сложным типом данных, то нам пришлось бы использовать deepSeq при записи экземпляра NFData.

В Color2 это немного проще.Из-за паттернов взрыва мы знаем, что компоненты полностью оцениваются, когда Color2.Это означает, что мы можем использовать реализацию по умолчанию, которая оценивает Color2 в слабую нормальную форму головы, которая из-за шаблонов взрыва полностью оценивается.

rnf в основном полезна при использовании в сочетании с Control.Parallel.Strategies.Вот текущее определение deepSeq

deepseq :: NFData a => a -> b -> b
deepseq a b = rnf a `seq` b

Все, что делает Deepseq, вызывает rnf и гарантирует, что его output () оценен.Это действительно единственный способ использовать rnf напрямую.

[1] Haskell предоставляет только два основных способа оценки материала: сопоставление с образцом и seq.Все остальное построено на одном или обоих из них.Для экземпляра NFData Color Color сначала оценивается в WHNF путем сопоставления с шаблоном с помощью конструктора Color, затем компоненты оцениваются с помощью seq.

Конечно, есть и третий, узкоспециализированный способоценивать вещи: т.е. будет выполняться функция main :: IO () для оценки ().

...