如何在Haskell中构建有状态模块?

时间:2011-01-24 18:01:50

标签: haskell

我正在寻找一个允许Haskell程序与Cassandra交互的通用模块。该模块需要保持自己的状态。例如,它将具有连接池和保存新记录时要调用的回调列表。我应该如何构造代码以使该模块能够保持其状态?以下是我一直在考虑的一些方法。我是在正确的轨道上吗? (我是Haskell的新手,仍在学习功能性思考的最佳方法。)

选项1:

模块在(StateT的IO)monad中运行,其中s是使用Cassandra模块的整个程序的全局状态。当然,由于Cassandra模块可以被多个程序使用,因此对于Cassandra模块来说,s中的内容的细节应该是不可见的。该模块必须导出一个类型类,允许它从s中提取CassandraState并将新的CassandraState推回到s中。然后,任何使用该模块的程序都必须使其主状态成为此类型类的成员。

选项2:

模块在(StateT CassandraState IO)monad中运行。每当有人在模块中调用一个动作时,他们就必须从隐藏它的任何地方提取CassandraState,用runState调用动作,然后取出结果状态并将其再次存放(无论哪里)。

选项3:

根本不要将Cassandra模块的函数放在StateT monad中。相反,让调用者在需要时显式传入CassandraState。选项2的问题在于并非模块中的所有功能都将修改状态。例如,获取连接将修改状态,并要求调用者存储结果状态。但是,保存新记录需要读取状态(以获取回调),但不需要更改状态。选项2不会给调用者任何提示连接更改状态而创建没有。

但是,如果我不再使用StateT monad并且只是具有将状态作为参数并且返回简单值或简单值和新状态的元组的函数,那么当状态需要时调用者真的很明显被救了(在我的模块的封面下,我将传入状态并将它们构建到(StateT CassandraState IO)monad中,但是这个细节会被调用者隐藏。所以,对于调用者来说,接口是非常明确的,但在封面下,它只是选项2。)

选项4:

还有别的吗?

在构建可重用模块时,必须经常出现此问题。是否有某种标准的解决方法?

(顺便说一句,如果有人知道从Haskell与Cassandra交互比使用Thrift更好的方式,请告诉我!也许我根本不需要写这个。: - )

2 个答案:

答案 0 :(得分:9)

类似HDBC模型的是具有明确的CassandraConnection数据类型。它内部有一个MVar,有一些可变状态。因为我想你所有的行动都在IO中,所以他们可以把CassandraConnection作为这些行为的参数。然后,用户可以将该连接打包到状态或读取器monad中,或者明确地将其打包,或者做他们想做的任何事情。

在内部你可以使用monad - 这真的是你的电话。但是,我赞成API尽可能不强迫用户使用任何特定的monad,除非真的有必要。

所以这是选项3的一种版本。但是用户不应该真正关心他们是否正在改变连接状态 - 在那个级别你可以真正隐藏它们的细节。

答案 1 :(得分:3)

我选择选项2.模块的用户不应直接使用runState;相反,您应该提供一个不透明的Cassandra类型,其中包含Monad类型类的实例和一些runCassandra :: Cassandra a -> IO a操作来“逃避”Cassandra。模块导出的操作应全部在Cassandra monad(例如doSomethingInterestingInCassandra :: Int -> Bool -> Cassandra Char)中运行,其定义可以访问已包装的CassandraState

如果您的用户需要一些额外的状态用于他们的应用程序,他们总是可以围绕Cassandra包裹monad变换器,例如StateT MyState Cassandra