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

Ell*_*ron 14 resources haskell exception-handling monad-transformers io-monad

无论好坏,Haskell流行的Servant库使得在monad变换器堆栈中运行代码变得很普遍ExceptT err IO.仆人自己的处理程序monad是ExceptT ServantErr IO.正如许多人所说,这是一个有点麻烦的monad工作,因为有多种方法可以展开失败:1)通过IO基地的正常例外,或2)返回Left.

正如Ed Kmett的exceptions图书馆有助于澄清:

基于连续的monad和诸如ErrorT e IO提供多种故障模式的堆栈是this [ MonadMask]类的无效实例.

这非常不方便,因为MonadMask我们可以访问有用的[多态版本] bracket函数来进行资源管理(不会因异常而泄漏资源等).但是在Servant的Handlermonad我们不能使用它.

我对它不是很熟悉,但是有些人说解决方案是使用monad-control它和许多合作伙伴库一样,lifted-base并且lifted-async让你的monad访问资源管理工具bracket(大概这适用于ExceptT err IO和朋友一样?).

然而,似乎monad-control失去了在社会上赞成,但我不能告诉替代将是什么.即使Snoyman最近的safe-exceptions图书馆使用Kmett的exceptions库也避免了monad-control.

有人可以为像我这样试图为严重的Haskell使用方式的人们澄清当前的故事吗?

dan*_*iaz 6

你可以工作IO,IO (Either ServantErr r)在结尾处返回一个类型的值并将其包装起来ExceptT以使其适合处理程序类型.这将让你bracket正常使用IO.这种方法的一个问题是您失去了提供的"自动错误管理" ExceptT.也就是说,如果你在处理程序的中间失败,你将不得不在这样的Either事情上执行显式的模式匹配.


以上基本上是重新实现的MonadTransControl实例ExceptT,即

instance MonadTransControl (ExceptT e) where
    type StT (ExceptT e) a = Either e a
    liftWith f = ExceptT $ liftM return $ f $ runExceptT
    restoreT = ExceptT
Run Code Online (Sandbox Code Playgroud)

monad-control在提升函数时工作正常bracket,但它有奇怪的角落案例,其功能如下(摘自本博文):

import Control.Monad.Trans.Control

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

callTwice' :: ExceptT () IO () -> ExceptT () IO ()
callTwice' = liftBaseOp_ callTwice
Run Code Online (Sandbox Code Playgroud)

如果我们传递给callTwice'打印某些东西的动作并在之后立即失败

main :: IO ()
main = do
    let printAndFail = lift (putStrLn "foo") >> throwE ()
    runExceptT (callTwice' printAndFail) >>= print  
Run Code Online (Sandbox Code Playgroud)

无论如何,它打印"foo"两次,即使我们的直觉说它应该在第一次执行动作失败后停止.


另一种方法是使用resourcet库并在ExceptT ServantErr (ResourceT IO) rmonad中工作 .您将需要使用resourcet的功能,如allocate,而不是bracket到了最后,并调整单子,如:

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

adapt :: ExceptT ServantErr (ResourceT IO) r -> ExceptT err IO r 
adapt = ExceptT . runResourceT . runExceptT
Run Code Online (Sandbox Code Playgroud)

或者喜欢:

import Control.Monad.Morph

adapt' :: ExceptT err (ResourceT IO) r -> ExceptT err IO r 
adapt' = hoist runResourceT
Run Code Online (Sandbox Code Playgroud)