Haskell 把圣莫纳德换成与州莫纳德相似的样子
这是一个场景:给定的是一个C库,它的核心是一些结构,其上的操作由丰富的C函数提供 步骤1:使用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
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
。实际上已经是单子了,但是states
不能逃避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
工作所必需的。我无法根据您的建议提出解决方案。最后,必须有一些monadM
,这样才能使用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