Haskell中bracket的after调用时机及mybracket正确性求证
Haskell bracket函数执行逻辑与自定义实现疑问
一、原bracket函数为何只执行一次after?
先看标准库的bracket实现:
bracket :: IO a -- ^ 先执行的资源获取逻辑 -> (a -> IO b) -- ^ 最后执行的资源释放逻辑 -> (a -> IO c) -- ^ 中间执行的业务逻辑 -> IO c -- 返回中间逻辑的结果 bracket before after thing = mask $ \restore -> do a <- before r <- restore (thing a) `onException` after a _ <- after a return r
测试代码:
bracket (pure 1) (\a -> print a >> pure a) (\a -> head [])
执行后print只输出一次,核心原因在于onException的行为逻辑:
关键代码段执行逻辑
r <- restore (thing a) `onException` after a _ <- after a
- 当
thing a(此处为head [])抛出异常时:onException捕获异常,执行after a(即print 1);- 执行完成后,
onException会重新抛出捕获到的异常; - 异常抛出后,后续的
_ <- after a代码不会被执行,因此after仅调用一次。
- 当
thing a正常执行完成时:onException不会触发,直接获取thing a的结果;- 继续执行后续的
_ <- after a,此时after也仅调用一次。
简言之,onException内的after和后续的after是互斥执行的,永远不会同时触发,所以无论是否发生异常,after只会被调用一次。
二、自定义mybracket能否保证after只调用一次?
自定义实现代码:
mybracket :: IO a -- ^ 资源获取逻辑 -> (Maybe c -> a -> IO c) -- ^ 资源释放逻辑(带执行状态标记) -> (a -> IO c) -- ^ 中间业务逻辑 -> IO c -- 返回释放逻辑的结果 mybracket before after thing = mask $ \restore -> do a <- before r <- restore (thing a) `onException` after Nothing a r1 <- after (Just r) a return r1
可以保证无论thing是否抛出异常,after只会被调用一次:
- 当
thing a抛出异常时:onException触发,调用after Nothing a;- 随后异常被重新抛出,后续的
after (Just r) a不会执行,after仅调用一次。
- 当
thing a正常执行时:onException不触发,直接拿到结果r;- 执行
after (Just r) a,after也仅调用一次。
该实现通过Maybe c标记释放逻辑的执行场景(异常/正常),同时利用onException的异常传播特性,确保两个分支互斥,不会重复调用after。
内容的提问来源于stack exchange,提问作者RandomB
相关产品推荐
相关产品推荐

