Haskell:Yesod和状态 [英] Haskell: Yesod and state

查看:110
本文介绍了Haskell:Yesod和状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读玩具URL缩短器。但是,有很多地方是我无法理解的。

I was reading through the code for a Toy URL Shortener. However, there's significant parts I just can't get my head around.

它具有以下代码:

data URLShort = URLShort { state :: AcidState URLStore }

出于测试目的,我在自己的应用中写了这样的内容:

For testing purposes, I wrote something like this in my own app:

data MyApp = MyApp { state :: Int }

然后我可以通过更改进行编译

I could then compile by changing

main = warpDebug 3000 MyApp

main = warpDebug 3000 (MyApp 42)

然后我可以通过执行以下操作来读取处理程序中的状态

And I could then do reads of the state in handlers by doing

mystate <- fmap state getYesod 

acid<-fmap状态getYesod 的启发文章。但是,我不知道该怎么写。

inspired by acid <- fmap state getYesod in the article. However, I didn't know how to do writes.

我也尝试过这样做:

data MyApp = MyApp { state :: State Int Int }

但是我

我试图通过做一些简单的事情来弄清楚 AcidState 是如何工作的类似的例子,假设由于 AcidState 将所有内容都保留在内存中,我应该能够执行相同的操作吗?

I was trying to work out how AcidState works just by doing some simple similar examples, figuring that since AcidState keeps everything in memory, I should be able to do the same?

任何

推荐答案

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

The AcidState a data type is not an immutable value all the way down; it contains references to mutable data internally. What is stored in Yesod-land in this case is simply an immutable reference to this data. When you update the state, you don't actually update the value in the foundation data type, but instead the memory that it points to.

Haskell世界中的每个值是一成不变的。但是,Haskell领域之外的许多事情并非一成不变的;例如,当您执行 putStrLn 时,终端会将其显示更改为显示新内容。 putStrLn 动作本身是一个不变的纯值,但它描述如何执行涉及突变的动作。

Every value in the Haskell world is immutable. However, a lot of things outside of the Realm of Haskell isn't immutable; for example, when you do putStrLn, the terminal mutates its display to show new content. The putStrLn action itself is an immutable pure value, but it describes how to perform an action involving mutation.

还有其他一些功能也会产生执行突变的动作;如果执行 ref<-newIORef 0 ,则会得到一个值,该值描述了创建可变存储器单元的操作。如果随后执行 modifyIORef ref(+1),则会得到一个值,该值描述的操作会使该单元格中的值增加 1 ref 值是纯值,它只是对可变单元格的引用。该代码也是纯功能性的,因为每一段都只描述一个动作。在Haskell程序中没有什么是可变的。

There are other functions that also yield actions that perform mutations; if you do ref <- newIORef 0, you get a value describing an action that creates a mutable memory cell. If you then do modifyIORef ref (+1), you get a value describing an action that increments the value in that cell by 1. The ref value is a pure value, it's simply a reference to a mutable cell. The code is also purely functional, because each piece only describes an action; nothing is mutable within the Haskell program.

这就是 AcidState 实现其状态的方式:通过使用管理Haskell世界之外的状态的系统。这不像C之类的语言那样具有完全可变性的缺点,因为在Haskell中,您可以使用monads的功能控制可变性。据我所知,使用 AcidState 是绝对安全的,并且不涉及使用 unsafePerformIO

This is how AcidState implements its state: by using a system that manages state outside of the Haskell world. This is not "as bad as" having full mutability like in languages such as C, because in Haskell, you can control the mutability with the power of monads. Using AcidState is perfectly safe and does not involve the use of unsafePerformIO as far as I know.

在这种情况下,如果使用 AcidState ,则在<$中使用 openAcidState emptyStore c $ c> IO monad以创建新的酸状态(该行是描述打开新酸状态的IO操作的值)。您可以使用 createCheckpointAndClose 将酸状态安全地保存到磁盘。最后,使用 update'函数来改变酸状态的内容。

With AcidState in this case, you use openAcidState emptyStore in the IO monad to create a new acid state (that line is a value describing an IO action that opens a new acid state). You use createCheckpointAndClose to optionally save the acid state to disk safely. Finally, you use the update' function to mutate the contents of an acid state.

要创建一个小状态,您可以使用 IORef (最简单的可变状态形式,除了 ST monad之外)将这样的字段添加到基础数据类型中:

To create a "small state" yourself using IORefs (The simplest form of mutable state, except for maybe the ST monad), you first add a field like this to your foundation data type:

data VisitorCounter = VisitorCounter { visitorCounter :: IORef Int }

然后执行以下操作:

main = do
  counter <- newIORef 0
  warpDebug 3000 (VisitorCounter counter)

在处理程序中,您可以像这样修改计数器:

In a handler, you can modify the counter like this:

counter <- fmap visitorCounter getYesod
modifyIORef counter (+1)
count <- readIORef counter
-- ... display the count or something

请注意 AcidState 的对称性。

对于站点计数器,我实际上建议使用 TVar s 而不是 IORef s,因为多个客户端可能同时修改变量。但是, TVar 的界面非常相似。

For a site counter, I would actually recommend using TVars instead of IORefs, because of the possibility that multiple clients might modify the variable simultaneously. The interface to TVars is very similar, however.

跟进问题作者的问题吗?

我放置了 {visitorCounter :: TVar Int} ,以及处理程序中的以下代码:

I've placed { visitorCounter :: TVar Int } in my foundation type, and the following code in the handler:

counter <- fmap visitorCounter getYesod
count <- readTVar counter

第一行编译正常,但第二行抛出此错误:

The first line compiles fine, but the second throws this error:


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


我该如何解决?

这篇关于Haskell:Yesod和状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆