- 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/
我有两种结构,Header 和Session,它们都符合协议(protocol)TimelineItem。 我有一个 Array 由 TimelineItem 组成,如下所示: [Header1, S
这个问题在这里已经有了答案: Multiple assignment and evaluation order in Python (11 个答案) 关闭 6 年前。 我刚接触python所以想问你
我试图找到一种方法来在 R 中获取 A、A、A、A、B、B、B、B、B 的所有可能的唯一排列的列表。 组合最初被认为是获得解决方案的方法,因此组合的答案。 最佳答案 我认为这就是你所追求的。 @bil
我怎样才能将两个给定的向量混合成一个新的向量,它以交替的顺序保存它们的值。 (f [a a] [b b]) ; > [a b a b] 这是我想到的: (flatten (map vector [:a
这是我的第一个问题,我开始学习Python。之间有区别吗: a, b = b, a + b 和 a = b b = a + b 当您在下面的示例中编写它时,它会显示不同的结果。 def fib(n):
这个问题在这里已经有了答案: Why is there an injected class name? (1 个回答) 12 个月前关闭。 我不知道如何解释: namespace A { struct
我尝试了一些代码来交换 Java 中的两个整数,而不使用第三个变量,使用 XOR。 这是我尝试过的两个交换函数: package lang.numeric; public class SwapVars
假设类 B 扩展类 A,并且我想为 B 声明一个变量。什么更有效?为什么? B b或 A b . 最佳答案 您混淆了两个不同的概念。 class B extends A { } 意味着B 是 A .
我不确定这个问题的标题是什么,这也可能是一个重复的问题。所以请相应地指导。 我是 python 编程的新手。我有这个简单的代码来生成斐波那契数列。 1: def fibo(n): 2: a =
我在谷歌上搜索了有关 dynamic_cast 的内容,我发现显式地将基类对象转换为派生类指针可能是不安全的。但是当我运行一些示例代码来检查它时,我没有收到任何错误。请在下面找到我的代码: class
这个问题在这里已经有了答案: What is this weird colon-member (" : ") syntax in the constructor? (14 个答案) 关闭 8 年前。
在不重现产生非整数值的表达式的情况下实现以下目标的惯用方法是什么(在我的真实情况下,该值是在我不想重现的冗长查询之后计算为百分比的): SELECT * FROM SomeTable WHERE 1/
在析构中,这两个代码的结果确实不同。我不确定为什么。 提示说 const [b,a] = [a,b] 将导致 a,b 的值为 undefined (从左到右的简单分配规则)。我不明白为什么会这样。 l
C++ Templates - The Complete Guide, 2nd Edition介绍max模板: template T max (T a, T b) { // if b < a th
我最近开始学习代码(Java),并根据第 15.17.3 节在 Oracle 网站上查找了模运算符。以下链接: http://docs.oracle.com/javase/specs/jls/se8/
无法理解以下行为。 d1 := &data{1}; 的区别d1 和 d2 := 数据{1}; &d1。两者都是指针,对吧?但他们的行为不同。这里发生了什么 package main import "f
这个问题在这里已经有了答案: How to make loop infinite with "x = y && x != y"? (4 个回答) How can i define variables
在我的程序中,当我调试我的代码时,它似乎在我生成的代码中的某处 X1=['[a,a,a]','[b,b,b]'] 还有我生成的其他地方 X2=[[a,a,a],[b,b,b]] 当我想添加这两个列表然
我试图使用递归将两个整数相乘,并意外编写了这段代码: //the original version int multiply(int a, int b) { if ( !b ) retu
我有一个列表中数字之间所有可能的操作组合: list = ['2','7','8'] 7+8*2 8+7*2 2*8+7 2+8*7 2-8*7 8-2/7 etc 我想知道是否可以说像 ('7*2+
我是一名优秀的程序员,十分优秀!