将ST monad重新装扮成类似于州Monad的东西

时间:2018-06-03 05:43:37

标签: haskell ffi state-monad st-monad

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

第1步:使用Haskell FFI创建一个包装器。它具有myCLibInit :: IO MyCLibObjmyCLibOp1 :: MyCLibObj -> ... -> IO ()等功能。 MyCLibObj是一种opaque类型,它将PtrForeignPtr传递(并隐藏)到实际的C结构中,例如,如wiki或{{3}所示}。

第2步:使用RWH ch. 17中的unsafeIOToST将所有IO操作转换为Control.Monad.ST.Unsafe操作。这是通过引入类似

的东西来完成的
 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步:通常,在一个do-block中将操作混合在几个MyCLibObj上是没有意义的。例如,当C结构是(或应该被认为是)单例实例时。在上面的doSomething中执行某些操作要么是无意义的,要么是纯粹禁止的(例如,当C结构是static时)。在这种情况下,类似于ST 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(对于&#39; uestion),应该是

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

这对我来说非常奇怪。我已经在努力为此Functor实施Q个实例,更不用说ApplicativeMonad了。 State实际上已经是monad,但状态s无法逃脱ST monad,因此forall s. ST s a。这是摆脱s的唯一方法,因为runST :: (forall s. ST s a) -> a,而withMyCLibInstance只是myCLibInit'后跟runST。但不知怎的,这不合适。

解决第3步的正确方法是什么?我是否应该在第1步之后立即执行第2步或滚动Q?我的感觉是,这应该很简单。我需要ST monad,Q只需要以正确的方式设置......

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

1 个答案:

答案 0 :(得分: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
如果您希望继续访问其他s功能(如可变引用和数组),则

MyCLibST应显示为ST的参数。