4

场景如下:Given 是一个 C 库,其核心是一些结构,其上的操作由大量 C 函数提供。

第 1 步:使用 Haskell 的FFI创建一个包装器。它具有myCLibInit :: IO MyCLibObjmyCLibOp1 :: MyCLibObj -> ... -> IO ()等功能。MyCLibObj是一个不透明类型,它携带(并隐藏) aPtrForeignPtr实际的 C 结构,例如在本wikiRWH ch. 17 .

第 2 步:使用unsafeIOToSTfromControl.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 结构是 a 时static)。在这种情况下,类似于Statemonad 的语言是必要的:

doSomething :: ResultType
doSomething =  withMyCLibInstance $ do
    success <- myCLibOp1'' "some-other-input"
    result <- myCLibOp2'' 42
    ...
    return result

在哪里

withMyCLibInstance :: Q a -> a

这就引出了一个问题:如何将ST s amonad 重新打扮成更像Statemonad 的东西。由于withMyCLibInstance将使用runST新的 monad 函数,我们称之为Q(对于 'q'uestion),应该是

newtype Q a = Q (forall s. ST s a)

这对我来说看起来很奇怪。我已经在Functor为此实现实例而苦苦挣扎Q,更不用说ApplicativeMonad. ST s实际上已经是一个 monad,但是 states不能逃脱STmonad,因此forall s. ST s a. 这是摆脱的唯一方法,s因为runST :: (forall s. ST s a) -> a,并且withMyCLibInstance只是 amyCLibInit'后跟 a runST。但不知何故,这不合适。

处理第 3 步的正确方法是什么?我应该做第 2 步,还是Q在第 1 步之后滚动我的权利?我的感觉是这应该很简单。ST单子有我需要的一切,只是Q需要以正确的方式设置......

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

4

1 回答 1

5

您可以使用阅读器效果来访问单例,仅在run函数中实例化它:

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

sMyCLibST如果您想继续访问其他ST功能(如可变引用和数组) ,则应该显示为参数。

于 2018-06-03T14:22:25.677 回答