There is a good known function - bracket:
bracket
:: IO a -- ^ computation to run first (\"acquire resource\")
-> (a -> IO b) -- ^ computation to run last (\"release resource\")
-> (a -> IO c) -- ^ computation to run in-between
-> IO c -- returns the value from the in-between computation
bracket before after thing =
mask $ \restore -> do
a <- before
r <- restore (thing a) `onException` after a
_ <- after a
return r
I see two calls of after: once on an exception, the second time - _ <- after a - in all cases (with exception/without exception). I call this:
bracket (pure 1) (\a -> print a >> pure a) (\a -> head [])
And print happens only once. Then why, with 2 afters? How does it work at all?
Could somebody explain in details how this snippet:
r <- restore (thing a) `onException` after a
_ <- after a
work exactly?
ps. I tried bang (!..) patterns, and nothing changed, it seems it is not about lazy evaluation...
EDIT: experiment with own (more specific logic) implementation:
mybracket
:: IO a -- ^computation to run first (\"acquire resource\")
-> (Maybe c -> a -> IO c) -- ^computation to run last (\"release resource\")
-> (a -> IO c) -- ^computation to run in-between
-> IO c -- returns the value from the in-between computation
mybracket before after thing =
mask $ \restore -> do
a <- before
r <- restore (thing a) `onException` after Nothing a
r1 <- after (Just r) a
return r1
Can I be sure that mybracket will call after just once - for exception in thing and when no any exceptions, in both cases?
CodePudding user response:
onException re-throws the exception. So in this snippet,
-- if
-- `action` returns normally | `action` throws an exception
-- then
r <- action `onException` after -- this `after` doesn't run | this `after` runs
_ <- after -- that `after` runs | that `after` doesn't run
...
... either action succeeds, then the `onException` after is ignored and only _ <- after runs. Or action throws an exception, then after `onException` after re-throws the exception after running after, and _ <- after doesn't run.
