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使用方式的人们澄清当前的故事吗?
你可以工作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)