You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 [])抛出异常时:
    1. onException捕获异常,执行after a(即print 1);
    2. 执行完成后,onException会重新抛出捕获到的异常;
    3. 异常抛出后,后续的_ <- after a代码不会被执行,因此after仅调用一次。
  • 当thing a正常执行完成时:
    1. onException不会触发,直接获取thing a的结果;
    2. 继续执行后续的_ <- 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抛出异常时:
    1. onException触发,调用after Nothing a;
    2. 随后异常被重新抛出,后续的after (Just r) a不会执行,after仅调用一次。
  • 当thing a正常执行时:
    1. onException不触发,直接拿到结果r;
    2. 执行after (Just r) a,after也仅调用一次。

该实现通过Maybe c标记释放逻辑的执行场景(异常/正常),同时利用onException的异常传播特性,确保两个分支互斥,不会重复调用after。


内容的提问来源于stack exchange,提问作者RandomB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 00:41:03