Haskell 把圣莫纳德换成与州莫纳德相似的样子

Haskell 把圣莫纳德换成与州莫纳德相似的样子,haskell,ffi,state-monad,st-monad,Haskell,Ffi,State Monad,St Monad,这是一个场景:给定的是一个C库,它的核心是一些结构,其上的操作由丰富的C函数提供 步骤1:使用Haskell的方法创建包装器。它具有类似于myCLibInit::IO MyCLibObj,myCLibOp1::MyCLibObj->…->IO(),等等MyCLibObj是一种不透明类型,它将Ptr或ForeignPtr携带(并隐藏)到实际的C结构中,例如,如本文或中所示 步骤2:使用来自的unsafeIOToST将所有IO操作转换为操作。这是通过引入以下内容来实现的 data STMyCLib

这是一个场景:给定的是一个C库,它的核心是一些结构,其上的操作由丰富的C函数提供

步骤1:使用Haskell的方法创建包装器。它具有类似于
myCLibInit::IO MyCLibObj
myCLibOp1::MyCLibObj->…->IO()
,等等
MyCLibObj
是一种不透明类型,它将
Ptr
ForeignPtr
携带(并隐藏)到实际的C结构中,例如,如本文或中所示

步骤2:使用来自的
unsafeIOToST
将所有
IO
操作转换为操作。这是通过引入以下内容来实现的

 data STMyCLib s = STMyCLib MyCLibObj
然后将所有
IO
函数包装到
ST
函数中,例如:

myCLibInit' :: ST s (STMyCLib s)
myCLibInit' = unsafeIOToST $ STMyCLib <$> myCLibInit
在哪里

这就引出了一个问题:如何将
ST s a
单子重新打扮成更像
状态
单子的东西。由于
with myclibinInstance
将使用
runST
函数,因此新的monad,我们称之为
Q
(用于'Q'uestion'),应该是

newtype Q a = Q (forall s. ST s a)
这在我看来很奇怪。我已经在努力实现这个
Q
Functor
实例,更不用说
Applicative
Monad
。实际上已经是单子了,但是state
s
不能逃避
ST
monad,因此所有s都是
。ST s a
。这是摆脱
s
的唯一方法,因为
runST::(对于所有的s.stsa)->a
,而
withMyCLibInstance
只是一个
myCLibInit'
后跟一个
runST
。但不知怎么的,这不合适

解决步骤3的正确方法是什么?我甚至应该执行步骤2,还是在步骤1之后立即滚动我的
Q
?我的感觉是这应该很简单。
ST
monad拥有我所需要的一切,
Q
只需以正确的方式设置即可


更新1:步骤3中的单例和静态结构示例不是很好。如果两个这样的do块并行执行,可能会发生非常糟糕的事情,即两个do块将在同一个C结构上并行工作。

您可以使用读卡器效果访问单例,仅在
运行
函数中实例化它:

newtype MyCLibST s a = MyCLibST { unMyCLibST :: ReaderT (STMyCLib s) (ST s) a }

runMyCLibST :: (forall s. MyCLibST s a) -> a
runMyCLibST m = runST (myCLibInit >>= runReaderT (unMyCLibST m))

-- Wrap the API with this.
unsafeMkMyCLibST :: (MyCLibObj -> IO a) -> MyCLibST s a

s
应该显示为
MyCLibST
的一个参数,如果您想保持对其他
ST
功能的访问,如可变引用和数组。

也许使用
myclibinstance::(对于所有s.Q s a)->a
,并相应地修改
Q
会更容易些?似乎您正试图隐藏
s
参数,这是
ST
工作所必需的。我无法根据您的建议提出解决方案。最后,必须有一些monad
M
,这样才能
使用MyClibinInstance::ma->a
,所以我们不会以
newtype ma=forall s结束。ST s a
?为什么需要单独的单子?你能不能用MyClibinInstance::(对于所有s.STMyClib s->ST s a)->a来代替,而不公开myCLibInit?如果我理解正确,那会导致类似于MyClibinInstance$\obj->do…。好的,这是向前迈出的一步。但是,我仍然必须始终在
do
中使用
obj
ReaderT
。。。这是一个非常好的观点。为什么不使用更简单的
类型MyCLibST a=forall s呢。ReaderT(STMyCLib s)(ST s)a
?带有量词的类型同义词有各种各样的问题:它们使类型推断变得困难,Haskell的类型系统不是非指示性的(
[MyCLibST a]
不是有效的类型,
MyCLibST
不是
单号
(因为类型同义词必须始终完全应用)。至于
type
newtype
的问题,这主要是一个品味问题,但是
type
使代码重用更容易,
newtype
可以更好地定义抽象边界。我认为
newtype
是更安全的默认值。
withMyCLibInstance :: Q a -> a
newtype Q a = Q (forall s. ST s a)
newtype MyCLibST s a = MyCLibST { unMyCLibST :: ReaderT (STMyCLib s) (ST s) a }

runMyCLibST :: (forall s. MyCLibST s a) -> a
runMyCLibST m = runST (myCLibInit >>= runReaderT (unMyCLibST m))

-- Wrap the API with this.
unsafeMkMyCLibST :: (MyCLibObj -> IO a) -> MyCLibST s a