packages feed

fb-util-0.1.0.1: Util/Reader.hs

{-
  Copyright (c) Meta Platforms, Inc. and affiliates.
  All rights reserved.

  This source code is licensed under the BSD-style license found in the
  LICENSE file in the root directory of this source tree.
-}

module Util.Reader
  ( catchR, catchR_
  , bracketR, bracketR_
  , withRetry
  ) where

import Control.Exception
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader

-- | Same as 'catch' from 'Control.Exception', but lifted into a ReaderT
catchR
  :: Exception e
  => ReaderT r IO a
  -> (e -> ReaderT r IO a)
  -> ReaderT r IO a
catchR (ReaderT action) handler = ReaderT $ \env ->
  action env `catch` \e -> runReaderT (handler e) env

-- | Same as 'catchR', but handler doesn't take an argument
catchR_
  :: ReaderT r IO a
  -> ReaderT r IO a
  -> ReaderT r IO a
catchR_ action handler = action `catchR` \SomeException{} -> handler

-- | Same as 'bracket' from 'Control.Exception', but lifted into a ReaderT
bracketR
  :: ReaderT r IO a        -- ^ before
  -> (a -> ReaderT r IO b) -- ^ after
  -> (a -> ReaderT r IO c) -- ^ computation
  -> ReaderT r IO c
bracketR before after thing = ReaderT $ \env ->
  bracket (runReaderT before env)
          (flip runReaderT env . after)
          (flip runReaderT env . thing)

-- | Same as 'bracket_' from 'Control.Exception', but lifted into a ReaderT
bracketR_
  :: ReaderT r IO a -- ^ before
  -> ReaderT r IO b -- ^ after
  -> ReaderT r IO c -- ^ computation
  -> ReaderT r IO c
bracketR_ before after thing =
  bracketR before (const after) (const thing)

-- | Retries the given computation up to n times if it fails.
-- WARNING: only use this with actions that have NO SIDE EFFECTS on failure
withRetry :: Int -> ReaderT r IO a -> ReaderT r IO a
withRetry n action = foldl catchR action $ replicate n $
  \e@SomeException{} -> do lift $ print e ; action