- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个连接到数据库的函数,然后运行查询。这些步骤中的每一个都会导致 IO (Either SomeErrorType SomeResultType)
。
我真正喜欢在学习 Haskell 时使用 Either
和类似的 monad 的一件事是能够使用像 >>=
这样的 monad 函数和像这样的组合子mapLeft
来简化许多预期错误状态的处理。
我在这里阅读博客文章、Control.Monad.Trans
文档以及关于 SO 的其他答案的期望是,我必须以某种方式使用转换器/升降机从 IO
上下文到 Either
上下文。
This answer特别是真的很好,但我很难将它应用到我自己的案例中。
我的代码的一个更简单的例子:
simpleVersion :: Integer -> Config -> IO ()
simpleVersion id c =
connect c >>= \case
(Left e) -> printErrorAndExit e
(Right conn) -> (run . query id $ conn)
>>= \case
(Left e) -> printErrorAndExit e
(Right r) -> print r
>> release conn
我的问题是 (a) 我并没有真正理解 ExceptT
如何将我带到与 mapLeft handleErrors $ anyErrorOrResult >>= someOtherErrorOrResult >>= 类似的地方的机制打印
世界; (b) 我不确定如何确保始终以最好的方式释放连接(即使在我上面的简单示例中),尽管我想我会使用 bracket pattern .
我确信每个(相对)新的 Haskeller 都这么说,但我仍然真的不了解 monad 转换器,而且我读到的所有内容(除了前面链接的 SO 答案)对我来说都太不透明了(还)。
我怎样才能将上面的代码转换成可以消除所有这些嵌套和错误处理的东西?
最佳答案
我认为查看 Monad
的来源非常有启发性。 ExceptT
的实例:
newtype ExceptT e m a = ExceptT (m (Either e a))
instance (Monad m) => Monad (ExceptT e m) where
return a = ExceptT $ return (Right a)
m >>= k = ExceptT $ do
a <- runExceptT m
case a of
Left e -> return (Left e)
Right x -> runExceptT (k x)
如果您忽略 newtype
包装和展开,它变得更加简单:
m >>= k = do
a <- m
case a of
Left e -> return (Left e)
Right x -> k x
或者,您似乎不喜欢使用 do
:
m >>= k = m >>= \a -> case a of
Left e -> return (Left e)
Right x -> k x
你觉得这段代码很眼熟吗?这与您的代码之间的唯一区别是您编写 printErrorAndExit
而不是 return . Left
!所以,让我们移动 printErrorAndExit
到顶层,并且很高兴记住现在的错误而不是打印它。
simpleVersion :: Integer -> Config -> IO (Either Err ())
simpleVersion id c = connect c >>= \case (Left e) -> return (Left e)
(Right conn) -> (run . query id $ conn)
>>= \case (Left e) -> return (Left e)
(Right r) -> Right <$> (print r
>> release conn)
除了我所说的更改之外,您还必须粘贴 Right <$>
最后从 IO ()
转换对 IO (Either Err ())
采取行动行动。 (稍后会详细介绍。)
好的,让我们尝试替换我们的 ExceptT
从上面绑定(bind) IO
绑定(bind)。我将添加一个 '
区分ExceptT
IO
的版本版本(例如 >>=' :: IO (Either Err a) -> (a -> IO (Either Err b)) -> IO (Either Err b)
)。
simpleVersion id c = connect c >>=' \conn -> (run . query id $ conn)
>>=' \r -> Right <$> (print r
>> {- IO >>! -} release conn)
这已经是一种改进,一些空白更改使它变得更好。我还将包括一个 do
版本。
simpleVersion id c =
connect c >>=' \conn ->
(run . query id $ conn) >>=' \r ->
Right <$> (print r >> release conn)
simpleVersion id c = do
conn <- connect c
r <- run . query id $ conn
Right <$> (print r >> release conn)
对我来说,这看起来很干净!当然,在 main
,你还是想printErrorAndExit
,如:
main = do
v <- runExceptT (simpleVersion 0 defaultConfig)
either printErrorAndExit pure v
现在,关于 Right <$> (...)
...我说我想从 IO a
转换至IO (Either Err a)
.好吧,这种事情就是为什么 MonadTrans
类存在;让我们看看 ExceptT
的实现:
instance MonadTrans (ExceptT e) where
lift = ExceptT . liftM Right
好吧,liftM
和 (<$>)
是同一个函数,名字不同。所以如果我们忽略 newtype
包装和展开,我们得到
lift m = Right <$> m
!所以:
simpleVersion id c = do
conn <- connect c
r <- run . query id $ conn
lift (print r >> release conn)
您也可以选择使用 liftIO
如果你喜欢。区别在于 lift
总是通过一个变压器提升一元 Action ,但适用于任何一对包裹类型和变压器类型;而liftIO
解除IO
为您的 monad 变压器堆栈通过尽可能多的变压器进行操作,但仅适用于 IO
行动。
当然,到目前为止,我们已经省略了所有 newtype
包装和展开。对于simpleVersion
要像我们最后一个示例中一样漂亮,您需要更改 connect
和 run
酌情包含这些包装器。
关于haskell - 如何使用 exceptT 替换大量 IO(a b),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69327798/
现实世界 Haskell 指出 "Transformer stacking order is important" 。但是,我似乎无法弄清楚 ExceptT (ResourceT m) a 和 Res
Monad 转换器很棘手,我不确定(=没有良好的直觉)哪一个应该放在最上面。 最佳答案 StateT s (ExceptT e m) 这说: 以m开头 添加异常(exception)情况 添加状态 现
在 ExceptT Monad 转换器堆栈中,throwE 之后的绑定(bind)表达式将不会被执行。 这是真的吗? 如果是这样,我如何配置编译器或我的工具来检测像这个例子一样的死代码? {-# LA
(这个问题类似于this question,但是那个问题只收到了推荐errors包的评论,我想知道更多细节。) 我正在开发一个程序,该程序使用两个不同的包,它们都返回类型 IO (Either e a
假设我有这段(可以说是误导)代码: import System.Environment (getArgs) import Control.Monad.Except parseArgs :: Excep
我要运行一些计算,这些计算会引发不同种类的异常。我如何将它们链接在do块中而不评估每一个,然后将结果匹配为Either ex result? type IntComp ex = ExceptT ex
最近的 cabal 安装升级了我的 transformers 版本来自0.3.0.0至 0.4.1.0 。此次升级带来了有关 ErrorT 的折旧警告。 文档不清楚这只是重命名还是有功能更改?为什么要
我有一个连接到数据库的函数,然后运行查询。这些步骤中的每一个都会导致 IO (Either SomeErrorType SomeResultType)。 我真正喜欢在学习 Haskell 时使用 Ei
在上一个问题中,我问 Why is there no MonadMask instance for ExceptT? 并得到了许多令人信服的答案,包括图书馆作者,为什么不存在合法的例子。 截至 201
Edward Kmett 的异常库不提供MonadMask ExceptT 的实例. Ben Gamari once asked about this然后得出结论,文档对此进行了解释。这是我能找到的最
我对 Except 很困惑,特别是因为网络上没有好的教程。我不知道如何将此函数从 ExceptT 转换为 Except: data Error = Empty deriving (Show) badF
无论好坏,Haskell 的流行 Servant库使得在涉及 ExceptT err IO 的 monad 转换器堆栈中运行代码变得很常见。 Servant 自己的处理程序 monad 是 Excep
假设我有一个像这样的 monadic 堆栈: import Control.Monad.Trans.Reader import Control.Monad.Trans.Except import Co
我是一名优秀的程序员,十分优秀!