首页 文章

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

提问于
浏览
4

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

Step 1: 使用Haskell的FFI创建了一个包装器 . 它具有 myCLibInit :: IO MyCLibObjmyCLibOp1 :: MyCLibObj -> ... -> IO () 等功能 . MyCLibObj 是一个opaque类型,它将 PtrForeignPtr 携带(并隐藏)到实际的C结构中,例如wikiRWH ch. 17中所示 .

Step 2: 使用 unsafeIOToSTControl.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
    ...

Step 3: 通常在一个do-block中将操作混合在几个 MyCLibObj 上是没有意义的 . 例如,当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

这导致 question :如何将 ST s a monad重新打扮成类似于 State monad的东西 . 由于 withMyCLibInstance 将使用 runST 函数新的monad,我们称之为 Q (对于'q' uestion),应该是

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

这看起来很奇怪 . 我已经在努力为 Q 实现 Functor 实例,更不用说 ApplicativeMonad 了 . ST s实际上已经是一个monad,但状态 s 一定不能逃脱 ST monad,因此 forall s. ST s a . 这是摆脱 s 的唯一方法,因为 runST :: (forall s. ST s a) -> awithMyCLibInstance 只是一个 myCLibInit' ,然后是 runST . 但不知怎的,这不合适 .

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

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

1 回答

  • 5

    您可以使用reader效果来访问单例,只在 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
    

    如果要保持对可变引用和数组等其他 ST 功能的访问, s 应作为 MyCLibST 的参数出现 .

相关问题