haskell - VTY-UI需要IO。我能让这件事发生吗?

标签 haskell monads monad-transformers

我正在尝试使用 VTY-UI 库构建 UI。 我还使用自定义 monad(一些 monad 相互堆叠)。 对于常规 IO 功能,这不是问题。我可以将它们提升到我的单子(monad)中。但是,VTY-UI 函数 onActivate 具有以下类型签名:

onActivate::Widget Edit -> (Widget Edit -> IO ()) -> IO ()

有没有办法将 Widget Edit -> MyMonad () 函数转换为 (Widget Edit -> IO ()),而无需包装和解开我的单子(monad)? 我不想将所有库的类型签名重写为 MonadIO m => m () 而不是 IO ()

最佳答案

函数liftBaseOpDiscard来自monad-control似乎可以解决问题:

import Control.Monad.Trans.Control 

type MyMonad a = ReaderT Int (StateT Int IO) a

onActivate' :: Widget Edit -> (Widget Edit -> MyMonad ()) -> MyMonad ()
onActivate' = liftBaseOpDiscard . onActivate 

该函数有一个 MonadBaseControl约束,但 IO 之上的 ReaderTStateT 已经具有该类型类的实例。

正如 liftBaseOpDiscard 的文档提到的,回调内状态的更改将被丢弃。

MonadBaseControl 允许您暂时将 monad 堆栈的上层隐藏到堆栈的基本 monad 的值中 ( liftBaseWith ),然后在需要时再次弹出它们 ( restoreM ) .

编辑:如果我们需要保留回调内发生的效果(例如状态的更改),一种解决方案是使用 IORef 来“模仿”状态作为 ReaderT 的环境。写入 IORef 的值不会被丢弃。 monad-unlift包就是围绕这个想法构建的。一个例子:

import Control.Monad.Trans.Unlift 
import Control.Monad.Trans.RWS.Ref
import Data.IORef

-- use IORefs for the environment and the state
type MyMonad a = RWSRefT IORef IORef Int () Int IO a

onActivate' :: Widget Edit -> (Widget Edit -> MyMonad ()) -> MyMonad ()
onActivate' we f = do 
    -- the run function will unlift into IO
    UnliftBase run <- askUnliftBase
    -- There's no need to manually "restore" the stack using
    -- restoreM, because the changes go through the IORefs
    liftBase $ onActivate we (run . f)

之后可以使用 runRWSIORefT 运行 monad .

关于haskell - VTY-UI需要IO。我能让这件事发生吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30014884/

相关文章:

Haskell 新手 : No instance for Show in map function

haskell - lambda 表达式的模式匹配

haskell - ghci 中的 liftM : why is there such difference?

Haskell - 是否有扩展的 monad 类型 [ m (a -> m b) -> m a -> m b ]

scala - free monad 应该基于的仿函数在哪里

haskell - 结合 RandT 和 MaybeT

haskell - 有条件地修改镜头的目标

haskell - ST 周围的 newtype 导致类型错误

haskell - Haskell 中不同的、交互的状态级别

haskell - 如何将可变向量放入状态单子(monad)