哈斯克尔:Yesod和州

时间:2012-04-04 08:30:23

标签: haskell state yesod acid

我正在阅读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将所有内容保存在记忆中,我应该能够做同样的事情吗?

对此处发生的事情的任何形式的一般性解释,以及也许我如何忽略这一点都将非常感激。

1 个答案:

答案 0 :(得分:7)

AcidState a数据类型不是一直是不可变的值;它内部包含对可变数据的引用。在这种情况下,Yesod-land中存储的内容只是对此数据的不可变引用。更新状态时,实际上并不更新基础数据类型中的值,而是更新它指向的内存。

Haskell世界中的每个值都是不可变的。但是,Haskell领域之外的很多东西都不是一成不变的;例如,当您执行putStrLn时,终端会改变其显示以显示新内容。 putStrLn操作本身是一个不可变的纯值,但它描述如何执行涉及变异的操作。

还有其他功能也会产生执行突变的动作;如果执行ref <- newIORef 0,则会得到一个描述创建可变内存单元格的操作的值。然后,如果您执行modifyIORef ref (+1),则会获得一个值,该值描述一个操作,该操作会使该单元格中的值增加1ref值是纯值,它只是对可变单元格的引用。代码也是纯函数,因为每个部分只描述一个动作;在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的对称性。

对于网站计数器,我实际上建议使用TVars而不是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

我该如何解决这个问题?