【发布时间】:2018-06-03 05:43:36
【问题描述】:
以下是场景:Given 是一个 C 库,其核心是一些结构,其上的操作由大量 C 函数提供。
第 1 步:使用 Haskell 的 FFI 创建一个包装器。它具有myCLibInit :: IO MyCLibObj、myCLibOp1 :: MyCLibObj -> ... -> IO ()等功能。 MyCLibObj 是一种不透明类型,它将Ptr 或ForeignPtr 携带(并隐藏)到实际的C 结构中,例如在wiki 或RWH ch. 17 中所示。
第 2 步: 使用 unsafeIOToST from Control.Monad.ST.Unsafe 将所有 IO 操作转换为 ST 操作。这是通过引入类似的东西来完成的
data STMyCLib s = STMyCLib MyCLibObj
然后将所有IO函数包装在ST函数中,例如:
myCLibInit' :: ST s (STMyCLib s)
myCLibInit' = unsafeIOToST $ STMyCLib <$> myCLibInit
这允许编写反映使用类似 OO 的 C 库的命令式程序,例如:
doSomething :: ST s Bool
doSomething = do
obj1 <- myCLibInit'
success1 <- myCLibOp1' obj1 "some-other-input"
...
obj2 <- myCLibInit'
result <- myCLibOp2' obj2 42
...
return True -- or False
main :: IO ()
main = do
...
let success = runST doSomething
...
第 3 步: 通常将多个 MyCLibObj 的操作混合在一个 do-block 中是没有意义的。例如,当 C 结构是(或应该被认为是)单例实例时。在上面的doSomething 中做类似的事情要么是荒谬的,要么是完全禁止的(例如,当 C 结构是 static 时)。在这种情况下,类似于State monad 之一的语言是必要的:
doSomething :: ResultType
doSomething = withMyCLibInstance $ do
success <- myCLibOp1'' "some-other-input"
result <- myCLibOp2'' 42
...
return result
在哪里
withMyCLibInstance :: Q a -> a
这就引出了一个问题:ST s a monad 怎么能被重新打扮成更类似于 State monad 的东西。由于withMyCLibInstance 将使用runST 函数新的monad,我们称之为Q(用于'q'uestion),应该是
newtype Q a = Q (forall s. ST s a)
这对我来说看起来很奇怪。我已经在为这个Q 实现Functor 实例而苦苦挣扎,更不用说Applicative 和Monad。 ST s 实际上已经是一个 monad,但是状态 s 不能逃脱 ST monad,因此是 forall s. ST s a。这是摆脱s 的唯一方法,因为runST :: (forall s. ST s a) -> a 和withMyCLibInstance 只是myCLibInit' 后跟runST。但不知何故,这不合适。
解决第 3 步的正确方法是什么?我应该执行第 2 步,还是在第 1 步之后立即滚动我的Q?我的感觉是这应该很简单。 ST monad 拥有我所需要的一切,Q 只需要以正确的方式设置...
更新 1: 步骤 3 中的单例和静态结构示例不是很好。如果两个这样的 do 块并行执行,可能会发生非常糟糕的事情,即两个 do 块将并行处理同一个 C 结构。
【问题讨论】:
-
也许使用
withMyCLibInstance :: (forall s. Q s a) -> a会更容易,并相应地修改Q?您似乎正在尝试隐藏s参数,这是ST工作所必需的。 -
我不太能够根据您的建议制定解决方案。最后,一定有一些monad
M这样withMyCLibInstance :: M a -> a,所以我们不是以newtype M a = forall s. ST s a结尾吗? -
为什么需要一个单独的 monad?你能改写
withMyCLibInstance :: (forall s. STMyClib s -> ST s a) -> a而不暴露myCLibInit吗? -
如果我理解正确,那将导致类似
withMyCLibInstance $ \obj -> do ...的内容。好的,这是向前迈出的一步。不过,我仍然必须一直在do中使用obj。
标签: haskell ffi state-monad st-monad