gpt4 book ai didi

haskell - 在像 exceptT a IO 这样的 monad 堆栈中管理资源的最佳方法是什么?

转载 作者:行者123 更新时间:2023-12-02 05:58:47 24 4
gpt4 key购买 nike

无论好坏,Haskell 的流行 Servant库使得在涉及 ExceptT err IO 的 monad 转换器堆栈中运行代码变得很常见。 Servant 自己的处理程序 monad 是 ExceptT ServantErr IO。正如许多人所说,这有点 troublesome monad因为有多种方式导致无法展开:1) 通过来自底层 IO 的正常异常,或 2) 通过返回 Left.

如 Ed Kmett 的 exceptions图书馆helpfully clarifies :

Continuation-based monads, and stacks such as ErrorT e IO which provide for multiple failure modes, are invalid instances of this [MonadMask] class.

这非常不方便,因为 MonadMask 使我们能够访问有用的 [多态版本] bracket 函数来进行资源管理(不会由于异常等而泄漏资源)。 )。但在 Servant 的 Handler monad 中我们无法使用它。

我对此不太熟悉,但有人说解决方案是使用 monad-control ,它有很多合作伙伴库,例如 lifted-baselifted-async 使您的 monad 能够访问 bracket 等资源管理工具(大概这也适用于 ExceptT err IO 和 friend ?)。

但是,monad-control 似乎是 losing favor in the community ,但我不知道替代方案是什么。甚至 Snoyman 最近的 safe-exceptions 库也使用 Kmett 的 exceptions 库并避免 monad-control

有人可以为像我这样试图认真使用 Haskell 的人澄清当前的故事吗?

最佳答案

您可以在IO中工作,在最后返回一个IO(Either ServantErr r)类型的值并将其包装在ExceptT中使其适合处理程序类型。这将使您可以在 IO 中正常使用 bracket。这种方法的一个问题是您失去了 ExceptT 提供的“自动错误管理”功能。也就是说,如果您在处理程序中间失败,则必须对 Either 以及类似的事情执行显式模式匹配。

<小时/>

上面基本上是重新实现ExceptTMonadTransControl实例,即

instance MonadTransControl (ExceptT e) where
type StT (ExceptT e) a = Either e a
liftWith f = ExceptT $ liftM return $ f $ runExceptT
restoreT = ExceptT

monad-control 在提升像 bracket 这样的函数时工作正常,但它有一些奇怪的极端情况,函数如下(摘自 this blog post ):

import Control.Monad.Trans.Control

callTwice :: IO a -> IO a
callTwice action = action >> action

callTwice' :: ExceptT () IO () -> ExceptT () IO ()
callTwice' = liftBaseOp_ callTwice

如果我们传递给 callTwice' 一个打印某些内容的操作,然后立即失败

main :: IO ()
main = do
let printAndFail = lift (putStrLn "foo") >> throwE ()
runExceptT (callTwice' printAndFail) >>= print

无论如何,它都会打印“foo”两次,即使我们的直觉表明它应该在第一次执行操作失败后停止。

<小时/>

另一种方法是使用 resourcet库并在 ExceptT ServantErr (ResourceT IO) r monad 中工作。您需要使用 resourcet 函数,例如 allocate而不是 bracket,并在末尾调整 monad,如:

import Control.Monad.Trans.Resource
import Control.Monad.Trans.Except

adapt :: ExceptT ServantErr (ResourceT IO) r -> ExceptT err IO r
adapt = ExceptT . runResourceT . runExceptT

或类似:

import Control.Monad.Morph

adapt' :: ExceptT err (ResourceT IO) r -> ExceptT err IO r
adapt' = hoist runResourceT

关于haskell - 在像 exceptT a IO 这样的 monad 堆栈中管理资源的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40372087/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com