我在Haskell中有以下功能:
memdb = -- load the contents of a database into memory as a Map
然后我有以下一行:
map (\x -> memdb ! x) values
我希望memdb只生成一次Map,而不是每次map
的迭代。我可以用这样的东西来做:
make_memdb = -- equivalent to memdb in previous example
memdb <- make_memdb
map (\x -> memdb ! x) values
但这意味着我必须将memdb
传递给使用它的每个函数。我有什么办法可以:
一个。避免在每次调用时重新计算memdb
或
湾将make_memdb
中生成的值保存为常量,以便我可以避免将其传递给使用它的每个函数?
答案 0 :(得分:13)
由于您的地图来自数据库,这意味着它不能是常量,因为应用程序的运行可能会有所不同。
但这意味着我必须将memdb传递给使用它的每个函数。
是的,但是有一些工具可以让它听起来不那么糟糕。特别是,这听起来像是the reader monad的完美用例!
当您有一些值(如配置)时,通常会使用Reader monad,您希望在程序开始时加载它,然后能够在程序周围访问它而无需一直显式传递它。以下是您将如何使用它的简短示例:
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 :(得分:1)
您似乎希望通过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
的全局可变变量。如果你不喜欢这个,并且你不喜欢传递参数,那么学习状态monad!我相信另一个答案会讨论这个问题,这是正确的解决方案。
答案 2 :(得分:0)