Есть ли способ запомнить значение в Haskell? - PullRequest
6 голосов
/ 27 августа 2011

У меня есть следующая функция в Haskell:

memdb = -- load the contents of a database into memory as a Map

И тогда у меня есть следующая строка:

map (\x -> memdb ! x) values

Я бы хотел, чтобы memdb генерировал Карту только один раз, а не на каждой итерации map. Я мог бы сделать это, используя что-то вроде этого:

make_memdb = -- equivalent to memdb in previous example
memdb <- make_memdb
map (\x -> memdb ! x) values

Но это будет означать, что мне придется передать memdb каждой функции, которая его использует. Есть ли способ, которым я могу:

а. избегайте пересчета memdb каждый раз, когда он называется ИЛИ

б. сохранить значение, полученное в make_memdb как константу, чтобы я не мог передавать его каждой функции, которая его использует?

Ответы [ 3 ]

13 голосов
/ 27 августа 2011

Поскольку ваша карта поступает из базы данных, это означает, что она не может быть константой, поскольку она может отличаться между запусками вашего приложения.

Но это будет означать, что мне придется передавать memdb каждой функции, которая его использует.

Да, но есть инструменты, которые сделают это не так плохо, как кажется. В частности, это звучит как идеальный вариант использования читающей монады !

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

main = do
    memdb <- make_memdb -- Get the memdb from the database once and for all
    runReaderT foo memdb

foo = do
    memdb <- ask -- Grab the memdb. Will not reload from the database
    liftIO $ putStrLn "Hello, world" -- IO actions have to be lifted
    -- [...]

Смотри также:

1 голос
/ 27 августа 2011

Вы, похоже, хотите получить memdb через IO как способ избежать передачи большего количества параметров, верно? Затем вы спрашиваете, можете ли вы (A) определить memdb, подразумевая, что это будет функция верхнего уровня, без дополнительных затрат на загрузку данных из БД, или (B), если вы можете сохранить загруженную структуру данных с глобальной областью действия.

Оба они выполнимы с IORef и unsafePerformIO для определения изменяемой глобальной переменной верхнего уровня. Я не предлагаю вам сделать это. Это неуклюжий и раздражающий рефакторинг. Тем не менее, я покажу вам, как все равно:

Предположим, у вас есть функция:

make_memdb :: IO (Map K V)

Вы можете объявить изменяемую переменную верхнего уровня:

import Data.Map as M
import Data.IORef

mRef :: IORef (Map K V)
mRef = unsafePerformIO $ newIORef M.empty
{-# NOINLINE mRef #-}

main = do
    m <- make_memdb
    writeIORef mRef m
    ... do  stuff using mRef ...

stuffUsingMRef ... = do
    memdb <- readIORef
    let vs = map (memdb !) values
    return vs

Обратите внимание, что ваши функции всегда будут жить в IO. Это потому, что вам нужно IO, чтобы прочитать глобальную переменную, в которую вы поместили memdb. Если вам не нравится это, и вам не нравится передавать параметры, тогда изучите монаду состояния! Я уверен, что другой ответ обсудит это, и это правильное решение.

0 голосов
/ 27 августа 2011
...