Haskell 除了IO之外,管理monad堆栈中资源的最佳方法是什么?
不管是好是坏,Haskell流行的库已经成为在monad transformer堆栈中运行涉及Haskell 除了IO之外,管理monad堆栈中资源的最佳方法是什么?,haskell,exception-handling,resources,monad-transformers,io-monad,Haskell,Exception Handling,Resources,Monad Transformers,Io Monad,不管是好是坏,Haskell流行的库已经成为在monad transformer堆栈中运行涉及Except err IO的代码的常见场所。仆人自己的处理程序monad是除了ServantErr IO。正如许多人认为的那样,这是一个需要解决的问题,因为有多种方法可以使失败展开:1)通过基本的IO的正常异常,或2)通过返回Left 作为Ed Kmett的图书馆: 基于延续的monad和提供多种故障模式的堆栈(如error e IO)是该[MonadMask]类的无效实例 这非常不方便,因为Monad
Except err IO
的代码的常见场所。仆人自己的处理程序monad是除了ServantErr IO
。正如许多人认为的那样,这是一个需要解决的问题,因为有多种方法可以使失败展开:1)通过基本的IO
的正常异常,或2)通过返回Left
作为Ed Kmett的图书馆:
基于延续的monad和提供多种故障模式的堆栈(如error e IO
)是该[MonadMask
]类的无效实例
这非常不方便,因为MonadMask
允许我们访问有用的[polymorphic version]bracket
函数来进行资源管理(而不是由于异常等原因泄漏资源)。但是在仆人的Handler
monad中,我们不能使用它
我对它不是很熟悉,但有人说解决方案是使用monad控件
,它有许多合作伙伴库,如lifted base
和lifted async
,让monad访问资源管理工具,如bracket
(这大概适用于除了错误IO
和朋友吗?)
然而,monad控件
似乎是,但我不知道替代方案是什么。即使是斯诺曼最近的安全异常
库也使用Kmett的异常
库,避免使用monad控件
有人能为像我这样的人澄清一下当前的情况吗?他们正试图让我们认真使用Haskell?您可以在
IO
中工作,返回IO类型的值(要么是servanter,要么是servanter)
结尾,并将其包装在ExceptT
中,以使其适合处理程序类型。这将允许您在IO
中正常使用括号。这种方法的一个问题是您失去了“自动错误管理”“<代码>例外> <代码>提供。也就是说,如果在处理程序中间失败,则必须在<代码>上执行显式模式匹配。
以上内容基本上是为ExceptT
重新实现MonadTransControl
实例,即
instance MonadTransControl (ExceptT e) where
type StT (ExceptT e) a = Either e a
liftWith f = ExceptT $ liftM return $ f $ runExceptT
restoreT = ExceptT
monad控件在提升功能(如支架
)时工作正常,但它具有以下功能(取自):
如果我们传递给calltweep'
一个操作,该操作会打印一些内容,然后立即失败
main :: IO ()
main = do
let printAndFail = lift (putStrLn "foo") >> throwE ()
runExceptT (callTwice' printAndFail) >>= print
不管怎样,它会打印两次“foo”,即使我们的直觉说它应该在第一次执行操作失败后停止
另一种方法是使用库并在除ServantErr(ResourceT IO)r
单子中工作。您需要使用ResourceT
函数,如而不是括号
,并在末尾调整单子,如:
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
我的建议是:让你的代码活在IO中而不是ExceptT中,并将每个处理程序函数包装在一个ExceptT中。尝试一下非常好的响应!因此似乎没有真正的“标准”解决此问题的方法?在这种情况下,monad控件具有边缘情况是有道理的,因为根据Kmett的说法,它可能无法为这些堆栈提供良好的实现。在ExceptT err(ResourceT IO)r
和ResourceT(ExceptT err IO)之间有区别吗r
.AFAICSResourceT
只是一个阅读器,所以它应该可以通勤;但是还有其他含义吗?@phadej我相信这两个命令都可以,但我觉得除了err(ResourceT IO)r
更自然。经过深思熟虑和研究,我必须承认我得出了这个结论,在某些方面,这似乎是一个逃避,但却是最明确、最无痛的解决方案。仅仅因为它更简单并不意味着它是错的。我真的认为人们正在使整个问题变得比需要的更复杂,因为模糊的本埃菲茨。
import Control.Monad.Morph
adapt' :: ExceptT err (ResourceT IO) r -> ExceptT err IO r
adapt' = hoist runResourceT