Повторная оценка чистого выражения в 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 не выделялось повторно?


person antonakos    schedule 06.02.2011    source источник
comment
Вы пробовали прагму {-# NOINLINE #-}?   -  person fuz    schedule 06.02.2011
comment
@FUZxxl Да, {-# NOINLINE a #-} на локальном a этого не происходит.   -  person antonakos    schedule 06.02.2011
comment
Если вам нужна таблица поиска и вы хотите поделиться ею с повторяющимися действиями, конечно, вы должны отделить ее создание от функции @makeAction@? Похоже, что makeAction будет создавать массив каждый раз независимо от встраивания. Возможно, вам следует использовать IOArray, извлеченный из функции makeAction.   -  person stephen tetley    schedule 06.02.2011
comment
@stephen Мне нравится собирать вещи вместе, делая детали реализации, если и как используется таблица поиска. (Связанные) решения @nominolo и @bdonlan обеспечивают совместное использование a.   -  person antonakos    schedule 06.02.2011


Ответы (2)


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

{-# LANGUAGE BangPatterns #-}

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

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

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

    let a = listArray (0, 1000) [x ..]
    evaluate a
person nominolo    schedule 06.02.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)
person bdonlan    schedule 06.02.2011