我正在阅读Toy URL Shortener的代码。但是,有一些重要的部分我无法理解。
它有以下代码:
data URLShort = URLShort { state :: AcidState URLStore }
出于测试目的,我在自己的应用程序中写了这样的内容:
data MyApp = MyApp { state :: Int }
然后我可以通过更改
进行编译main = warpDebug 3000 MyApp
到
main = warpDebug 3000 (MyApp 42)
然后我可以通过
来读取处理程序中的状态mystate <- fmap state getYesod
受到文章中acid <- fmap state getYesod
的启发。但是,我不知道怎么写。
我也尝试过:
data MyApp = MyApp { state :: State Int Int }
但我对此没有太大的了解。
我试图通过做一些简单的类似例子来弄清楚AcidState
是如何工作的,并认为由于AcidState
将所有内容保存在记忆中,我应该能够做同样的事情吗?
对此处发生的事情的任何形式的一般性解释,以及也许我如何忽略这一点都将非常感激。
答案 0 :(得分:7)
AcidState a
数据类型不是一直是不可变的值;它内部包含对可变数据的引用。在这种情况下,Yesod-land中存储的内容只是对此数据的不可变引用。更新状态时,实际上并不更新基础数据类型中的值,而是更新它指向的内存。
Haskell世界中的每个值都是不可变的。但是,Haskell领域之外的很多东西都不是一成不变的;例如,当您执行putStrLn
时,终端会改变其显示以显示新内容。 putStrLn
操作本身是一个不可变的纯值,但它描述如何执行涉及变异的操作。
还有其他功能也会产生执行突变的动作;如果执行ref <- newIORef 0
,则会得到一个描述创建可变内存单元格的操作的值。然后,如果您执行modifyIORef ref (+1)
,则会获得一个值,该值描述一个操作,该操作会使该单元格中的值增加1
。 ref
值是纯值,它只是对可变单元格的引用。代码也是纯函数,因为每个部分只描述一个动作;在Haskell程序中没有任何可变的 。
这就是AcidState
实现其状态的方式:通过使用管理Haskell世界之外的状态的系统。这并不像C语言这样具有完全可变性“一样糟糕”,因为在Haskell中,你可以用monad的力量控制可变性。使用AcidState
非常安全,据我所知,不会涉及使用unsafePerformIO
。
在这种情况下使用AcidState
,您可以在openAcidState emptyStore
monad中使用IO
来创建新的酸状态(该行是描述打开新酸状态的IO操作的值)。您可以使用createCheckpointAndClose
来安全地将酸状态保存到磁盘。最后,使用update'
函数来改变酸状态的内容。
要使用IORef
(最简单形式的可变状态,可能是ST
monad)创建一个“小状态”,首先要将这样的字段添加到基础数据类型中:
data VisitorCounter = VisitorCounter { visitorCounter :: IORef Int }
然后你做:
main = do
counter <- newIORef 0
warpDebug 3000 (VisitorCounter counter)
在处理程序中,您可以像这样修改计数器:
counter <- fmap visitorCounter getYesod
modifyIORef counter (+1)
count <- readIORef counter
-- ... display the count or something
注意AcidState
的对称性。
对于网站计数器,我实际上建议使用TVar
s而不是IORef
,因为多个客户端可能会同时修改变量。但是,TVar
的界面非常相似。
来自问题作者的跟进问题?
我已将{ visitorCounter :: TVar Int }
放在我的基础类型中,以及处理程序中的以下代码:
counter <- fmap visitorCounter getYesod
count <- readTVar counter
第一行编译正常,但第二行抛出此错误:
Couldn't match expected type `GHandler sub0 Middleware t0' with actual type `STM a0' In the return type of a call of `readTVar' In a stmt of a 'do' expression: count <- readTVar counter
我该如何解决这个问题?