Повторная оценка чистого выражения в действии IO - PullRequest
5 голосов
/ 06 февраля 2011

У меня есть процедура, которая (a) выполняет некоторый IO, (b) создает таблицу поиска и (c) возвращает действие IO, которое использует таблицу поиска. Но при компиляции с -O GHC (версия 6.12.1) указывает на конструкцию таблицы соответствия, поэтому она переоценивается для каждого вызова действия ввода-вывода.

Пример:

module Main where
import Data.Array
import Data.IORef
import Control.Monad

makeAction getX getY sumRef = do
    x <- getX
    let a = listArray (0, 1000) [x ..]
    return $ do
        y <- getY
        modifyIORef sumRef (\sum -> sum + a ! y)

main = do
    sumRef <- newIORef 0
    action <- makeAction getX getY sumRef
    replicateM_ 100000 action
    n <- readIORef sumRef
    putStrLn (show n)
    where
    getX = return (1 :: Int)
    getY = return 0

Достаточно ли известна эта проблема, чтобы иметь стандартный обходной путь GHC - или как настроить программу так, чтобы a не выделялся повторно?

Ответы [ 2 ]

4 голосов
/ 06 февраля 2011

Самый простой обходной путь - форсировать оценку с помощью аннотаций строгости.

{-# LANGUAGE BangPatterns #-}

Затем форсируйте распределение, просто сделав a строгим, используя ! ("взрыв").

    let !a = listArray (0, 1000) [x ..]

В качестве альтернативы, если вы работаете в монаде IO, аннотации строгости могут не всегда помогать. Для принудительного вычисления выражения перед выполнением какого-либо действия IO вы можете использовать evaluate. Например:

    let a = listArray (0, 1000) [x ..]
    evaluate a
2 голосов
/ 06 февраля 2011

Попробуйте принудительно a при построении монадического значения для возврата:

makeAction getX getY sumRef = do
    x <- getX
    let a = listArray (0, 1000) [x ..]
    return $ a `seq` do
        y <- getY
        modifyIORef sumRef (\sum -> sum + a ! y)
...