The AcidState a
数据类型并不是一个始终不变的值;它包含对内部可变数据的引用。在这种情况下,Yesod-land 中存储的只是对此数据的不可变引用。当您更新状态时,您实际上并没有更新基础数据类型中的值,而是更新了它指向的内存。
Haskell 世界中的每个值都是不可变的。然而,Haskell 领域之外的许多事物并不是一成不变的。例如,当你这样做时putStrLn
,终端改变其显示以显示新内容。这putStrLn
行动本身是一个不可变的纯粹价值,但它描述如何执行涉及突变的操作。
还有其他函数也可以产生执行突变的操作;如果你这样做ref <- newIORef 0
,您将获得一个描述创建可变内存单元的操作的值。如果你那么做modifyIORef ref (+1)
,您将获得一个描述操作的值,该操作将该单元格中的值增加1
. The ref
值是一个纯价值,它只是对可变单元格的引用。代码也是纯功能性的,因为每个片段只描述一个动作;没有什么是可变的在 Haskell 程序中.
就是这样AcidState
实现它的状态:通过使用一个在 Haskell 世界之外管理状态的系统。这并不像 C 语言那样具有完全可变性“那么糟糕”,因为在 Haskell 中,你可以控制可变性凭借单子的力量。使用AcidState
是完全安全的并且不涉及使用unsafePerformIO
据我所知。
With AcidState
在这种情况下,您使用openAcidState emptyStore
in the IO
monad 创建一个新的酸性状态(该行是描述打开新酸性状态的 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
.
对于站点计数器,我实际上建议使用TVars http://hackage.haskell.org/packages/archive/stm/latest/doc/html/Control-Concurrent-STM-TVar.html#TVar代替IORef
s,因为多个客户端可能同时修改变量。接口为TVar
然而,s 非常相似。
跟进问题作者的问题?
我已经放置了{ 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
我该如何解决这个问题?