为 newtype-d CatchT (ST s) 栈实现 PrimMonad

Implementing PrimMonad for newtype-d CatchT (ST s) stack

我正在尝试使用 ST monad 从 xml-conduit 中获取 renderText。不幸的是,与 renderBytes 不同,它要求 monad 既是 PrimMonad 又是 MonadThrowIO 满足这个,但 ST 不满足。

renderText :: (PrimMonad m, MonadThrow m) => RenderSettings -> ConduitT Event Text m ()

我设法让它与 CatchT (ST s) a 堆栈一起工作,通过定义 PrimMonad 实例:

instance PrimMonad m => PrimMonad (CatchT m) where type PrimState (CatchT m) = PrimState m primitive = lift . primitive

这是一个运行状况不佳的孤立实例。我试图将它包装成新类型,但卡在 PrimMonad.

newtype Render a = Render { runRender :: forall s. MaybeT (ST s) a }

instance Functor Render where
    fmap f (Render m) = Render (fmap f m)

instance Applicative Render where
    pure a = Render (pure a)
    (Render f) <*> (Render v) = Render (f <*> v)

instance Monad Render where
    a >>= f = Render $ do
        v <- runRender a
        runRender (f v)

instance MonadThrow Render where
    throwM _ = Render $ MaybeT $ pure Nothing

instance PrimMonad Render where
   [???]

如何为这个堆栈定义 PrimMonad

更新:作为记录,这里是根据@luqui 的想法的答案。

newtype Render s a = Render { runRender :: MaybeT (ST s) a }

deriving instance Functor (Render s)
deriving instance Applicative (Render s)
deriving instance Monad (Render s)

instance MonadThrow (Render s) where
    throwM _ = Render $ MaybeT $ pure Nothing

instance PrimMonad (Render s) where
    type PrimState (Render s) = s
    primitive f = Render $ lift $ primitive f

您将需要公开 s 参数:

newtype Render s a = Render { runRender :: MaybeT (ST s) a }

A forall s. ST s a monad 看起来很吸引人,但它非常无用,因为(例如)newSTRef 不能让它创建的引用逃逸。 (尝试让 STRefs 与你的 monad 一起工作以查看问题)

公开 s 后,PrimMonad 实例应该很简单。

你也知道GeneralizedNewtypeDeriving,对吧?你不必做所有这些工作来制作一个新的包装器。