Да, в основном переопределить логику.Хотя это похоже на то, что GHC уже делает, это выбор GHC.Haskell может быть реализован на виртуальных машинах, которые работают совсем по-другому, поэтому в этом смысле это еще не сделано для вас.
Но да, просто используйте MVar (Map InputType OutputType)
или даже IORef (Map InputType OutputType)
(не забудьте изменитьс atomicModifyIORef
), и просто храните кеш там.Если это императивное решение кажется неправильным, это ограничение «если бы не IO
, эта функция была бы чистой».Если бы это было просто произвольное действие IO
, то идея о том, что вам нужно было бы сохранять состояние, чтобы знать, что выполнять или нет, кажется совершенно естественной.Проблема в том, что у Haskell нет типа для «чистого ввода-вывода» (который, если он зависит от базы данных, просто ведет себя чисто при определенных условиях, что не то же самое, что наследственно чистый).
import qualified Data.Map as Map
import Control.Concurrent.MVar
-- takes an IO function and returns a cached version
cache :: (Ord a) => (a -> IO b) -> IO (a -> IO b)
cache f = do
r <- newMVar Map.empty
return $ \x -> do
cacheMap <- takeMVar r
case Map.lookup x cacheMap of
Just y -> do
putMVar r cacheMap
return y
Nothing -> do
y <- f x
putMVar (Map.insert x y cacheMap)
return y
Да, уродливо внутри.Но снаружи посмотри на это!Это похоже на функцию чистой памяти, за исключением того, что на ней есть IO
.