haskell - 如何将上下文和 `IO` monad 隐藏到另一个 monad 中?

标签 haskell monads

我正在尝试使用 HDBCHaskell.GI 实现一个小型桌面应用程序。我使用 glade 构建窗口和对话框,并使用 GtkBuilder 加载它们。在实现了几个场景之后,我最终始终使用相同的模式,在 do block 中编写“ Action ”,其签名为:

Connection -> Builder -> a -> IO b

这些“ Action ”在 IO monad 的上下文中组合,主要问题是我必须传递我的 ConnectionBuilder在周围。我预见的另一个问题是,如果我想向我的应用程序添加另一个外部依赖项(例如,访问图像扫描仪),我将不得不更改我所有“ Action ”的签名,更重要的是,它们的数量。

我能做什么:我可以定义一个类型同义词:

type Action a b = Connection -> Builder -> a -> IO b

我还可以创建一个命名元组来消除元数问题:

data Context =
    Context {
        conn :: Connection,
        builder :: Builder}

但是,这仍然没有解决这样一个事实,即每次我想访问数据库时,我都必须调用 (conn ctx) 或使用 let在每个 Action 中都有约束力。

我觉得最理想的是制作我自己的 monad,我可以在其中编写我的 Action ,我不会明确地谈论我的 ConnectionBuilder 值.

如果知道 IO 已经是一个 monad,我该如何定义一个这样的 monad?

顺便说一句,它与 State monad 有什么关系吗?

最佳答案

[..] the main problem being that I have to pass my Connection and Builder all around.

所以这些是您(重复)阅读的“环境”的一部分。这就是 Reader monad 的用途。 mtl 包包含 monad 转换器 ReaderT它将阅读器功能添加到基本 monad,在您的例子中是 IO

演示:

假设一个简单的 Action ,比如..

no_action :: Connection -> Builder -> Int -> IO Int
no_action _ _ i = return (i + 1)

您可以将它放入一个类似于 IO 的新 Monad 中,但可以通过定义 Context 并应用 monad 转换器来访问连接和构建器:

data Context = Context { connection :: Connection
                       , builder :: Builder }
type CBIO b = ReaderT Context IO b

将你的 Action 提升到这个新的(组合的)monad 中值得一个单独的功能:

liftCBIO :: (Connection -> Builder -> a -> IO b) -> (a -> CBIO b)
liftCBIO f v = do
    context <- ask
    liftIO (f (connection context) (builder context) v)

然后你可以总是写(liftCBIO no_action) num 或者...

cbio_no_action = liftCBIO no_action

...和cbio_no_action num

要实际运行您的新 monad,您可以使用 runReaderT .. 但这也值得一个更好的名称:

runWithInIO = flip runReaderT

如果您愿意,您也可以更改它以合并构建Context

使用上面的代码看起来像这样:

main = do
    i <- runWithInIO (Context Connection Builder) $ do
        a <- cbio_no_action 20
        liftIO $ putStrLn "Halfway through!"
        b <- cbio_no_action 30
        return (a + b)
    putStrLn $ show i

(Full demo on ideone)

关于haskell - 如何将上下文和 `IO` monad 隐藏到另一个 monad 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55706425/

相关文章:

haskell - 使用 haskell monad 理解作用域

函数中的 Haskell 类型注释

Haskell 目录类型

haskell - 为隐藏构造函数的 Haskell 结构创建函数

Haskell:在源中指定版本的正确做法?

haskell - Haskell 是否有 `when` 和 `unless` 的组合?

haskell - 在这种情况下,为什么在与嵌套的 StateT monadT 交互时不需要使用 'lift'?

haskell - 如何在Haskell中释放一个可以在另一个程序中使用的模块?

Haskell——双重人格IO/ST monad?

Haskell + SDL,不理解函数/状态用法