AcidState a
数据类型一直不是不可变的值;它在内部包含对可变数据的引用。在这种情况下,存储在 Yesod-land 中的内容只是对该数据的不可变引用。更新状态时,实际上并没有更新基础数据类型中的值,而是更新它指向的内存。
Haskell 世界中的每一个值都是不可变的。然而,Haskell 领域之外的很多东西都不是一成不变的。例如,当您这样做时putStrLn
,终端会改变其显示以显示新内容。putStrLn
动作本身是不可变的纯值,但它描述了如何执行涉及突变的动作。
还有其他函数也产生执行突变的动作。如果你这样做ref <- newIORef 0
,你会得到一个描述创建可变记忆单元的动作的值。如果您这样做modifyIORef ref (+1)
,您将获得一个值,该值描述了将该单元格中的值增加 1 的操作1
。ref
value 是一个纯 value,它只是对可变单元格的引用。代码也是纯函数式的,因为每一段只描述一个动作;Haskell 程序中没有什么是可变的。
这是AcidState
实现其状态的方式:通过使用在 Haskell 世界之外管理状态的系统。这并不像在 C 等语言中那样具有完全可变性“那么糟糕”,因为在 Haskell 中,您可以使用 monad 的力量来控制可变性。使用是完全安全的,据我所知AcidState
不涉及使用。unsafePerformIO
在AcidState
这种情况下,您openAcidState emptyStore
在IO
monad 中使用来创建新的酸状态(该行是描述打开新酸状态的 IO 操作的值)。您createCheckpointAndClose
可以选择将酸状态安全地保存到磁盘。最后,您使用该update'
函数来改变酸性状态的内容。
要使用 s 自己创建一个“小状态” 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
s,因为多个客户端可能同时修改变量。然而,与 s 的接口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
我该如何解决这个问题?