首页 文章

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

提问于
浏览
9

我正在寻找一个允许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 回答

  • 9

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

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

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

  • 3

    我'd go with Option 2. Users of your module shouldn' t直接使用 runState ;相反,您应该提供一个不透明的 Cassandra 类型,其中包含 Monad 类型类的实例和一些 runCassandra :: Cassandra a -> IO a 操作到"escape" Cassandra . 模块导出的操作应该都在 Cassandra monad中运行(例如 doSomethingInterestingInCassandra :: Int -> Bool -> Cassandra Char ),并且它们的定义可以访问包装的 CassandraState .

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

相关问题