unliftio 0.2.13 → 0.2.13.1
raw patch · 5 files changed
+77/−43 lines, 5 files
Files
- ChangeLog.md +4/−0
- README.md +18/−25
- src/UnliftIO/Exception.hs +51/−14
- src/UnliftIO/Internals/Async.hs +2/−2
- unliftio.cabal +2/−2
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for unliftio +## 0.2.13.1++* Improve `UnliftIO.Exception` documentation+ ## 0.2.13 * Add `UnliftIO.STM.orElse`
README.md view
@@ -1,6 +1,6 @@ # unliftio -[](https://dev.azure.com/fpco/unliftio/_build/latest?definitionId=3&branchName=master)+ Provides the core `MonadUnliftIO` typeclass, a number of common@@ -131,31 +131,33 @@ pre-unlifted versions of functions (like `UnliftIO.Exception.catch`). But ultimately, you'll probably want to use the typeclass directly. The type class has only one method ---`askUnliftIO`:+`withRunInIO`: ```haskell-newtype UnliftIO m = UnliftIO { unliftIO :: forall a. m a -> IO a }- class MonadIO m => MonadUnliftIO m where- askUnliftIO :: m (UnliftIO m)+ withRunInIO :: ((forall a. m a -> IO a) -> IO b) -> m b ``` -`askUnliftIO` gives us a function to run arbitrary computation in `m`+`withRunInIO` provides a function to run arbitrary computations in `m` in `IO`. Thus the "unlift": it's like `liftIO`, but the other way around. Here are some sample typeclass instances: ```haskell instance MonadUnliftIO IO where- askUnliftIO = return (UnliftIO id)-instance MonadUnliftIO m => MonadUnliftIO (IdentityT m) where- askUnliftIO = IdentityT $- withUnliftIO $ \u ->- return (UnliftIO (unliftIO u . runIdentityT))+ withRunInIO inner = inner id+ instance MonadUnliftIO m => MonadUnliftIO (ReaderT r m) where- askUnliftIO = ReaderT $ \r ->- withUnliftIO $ \u ->- return (UnliftIO (unliftIO u . flip runReaderT r))+ withRunInIO inner =+ ReaderT $ \r ->+ withRunInIO $ \run ->+ inner (run . flip runReaderT r)++instance MonadUnliftIO m => MonadUnliftIO (IdentityT m) where+ withRunInIO inner =+ IdentityT $+ withRunInIO $ \run ->+ inner (run . runIdentityT) ``` Note that:@@ -163,21 +165,12 @@ * The `IO` instance does not actually do any lifting or unlifting, and therefore it can use `id` * `IdentityT` is essentially just wrapping/unwrapping its data- constructor, and then recursively calling `withUnliftIO` on the+ constructor, and then recursively calling `withRunInIO` on the underlying monad. * `ReaderT` is just like `IdentityT`, but it captures the reader environment when starting. -We can use `askUnliftIO` to unlift a function:--```haskell-timeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a)-timeout x y = do- (u :: UnliftIO m) <- askUnliftIO- liftIO $ System.Timeout.timeout x $ unliftIO u y-```--or more concisely using `withRunInIO`:+We can use `withRunInIO` to unlift a function: ```haskell timeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a)
src/UnliftIO/Exception.hs view
@@ -9,7 +9,7 @@ -- -- This module works best when your cleanup functions adhere to certain -- expectations around exception safety and interruptible actions.--- For more details, see [this exception safety tutorial](https://haskell.fpcomplete.com/tutorial/exceptions).+-- For more details, see [this exception safety tutorial](https://www.fpcomplete.com/haskell/tutorial/exceptions/). module UnliftIO.Exception ( -- * Throwing throwIO@@ -101,10 +101,17 @@ import GHC.Stack.Types (HasCallStack, CallStack, getCallStack) #endif --- | Unlifted 'EUnsafe.catch', but will not catch asynchronous exceptions.+-- | Catch a synchronous (but not asynchronous) exception and recover from it. --+-- This is parameterized on the exception type. To catch all synchronous exceptions,+-- use 'catchAny'.+-- -- @since 0.1.0.0-catch :: (MonadUnliftIO m, Exception e) => m a -> (e -> m a) -> m a+catch+ :: (MonadUnliftIO m, Exception e)+ => m a -- ^ action+ -> (e -> m a) -- ^ handler+ -> m a catch f g = withRunInIO $ \run -> run f `EUnsafe.catch` \e -> if isSyncException e then run (g e)@@ -118,7 +125,7 @@ catchIO :: MonadUnliftIO m => m a -> (IOException -> m a) -> m a catchIO = catch --- | 'catch' specialized to catch all synchronous exception.+-- | 'catch' specialized to catch all synchronous exceptions. -- -- @since 0.1.0.0 catchAny :: MonadUnliftIO m => m a -> (SomeException -> m a) -> m a@@ -182,8 +189,11 @@ handleJust :: (MonadUnliftIO m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a handleJust f = flip (catchJust f) --- | Unlifted 'EUnsafe.try', but will not catch asynchronous exceptions.+-- | Run the given action and catch any synchronous exceptions as a 'Left' value. --+-- This is parameterized on the exception type. To catch all synchronous exceptions,+-- use 'tryAny'.+-- -- @since 0.1.0.0 try :: (MonadUnliftIO m, Exception e) => m a -> m (Either e a) try f = catch (liftM Right f) (return . Left)@@ -235,7 +245,7 @@ pureTryDeep :: NFData a => a -> Either SomeException a pureTryDeep = unsafePerformIO . tryAnyDeep . return --- | Generalized version of 'EUnsafe.Handler'.+-- | A helper data type for usage with 'catches' and similar functions. -- -- @since 0.1.0.0 data Handler m a = forall e . Exception e => Handler (e -> m a)@@ -248,9 +258,11 @@ Just e' -> handler e' Nothing -> res --- | Same as upstream 'EUnsafe.catches', but will not catch--- asynchronous exceptions.+-- | Similar to 'catch', but provides multiple different handler functions. --+-- For more information on motivation, see @base@'s 'EUnsafe.catches'. Note that,+-- unlike that function, this function will not catch asynchronous exceptions.+-- -- @since 0.1.0.0 catches :: MonadUnliftIO m => m a -> [Handler m a] -> m a catches io handlers = io `catch` catchesHandler handlers@@ -274,8 +286,21 @@ evaluateDeep :: (MonadIO m, NFData a) => a -> m a evaluateDeep = (evaluate $!!) --- | Async safe version of 'EUnsafe.bracket'.+-- | Allocate and clean up a resource safely. --+-- For more information on motivation and usage of this function, see @base@'s+-- 'EUnsafe.bracket'. This function has two differences from the one in @base@.+-- The first, and more obvious, is that it works on any @MonadUnliftIO@+-- instance, not just @IO@.+--+-- The more subtle difference is that this function will use uninterruptible+-- masking for its cleanup handler. This is a subtle distinction, but at a+-- high level, means that resource cleanup has more guarantees to complete.+-- This comes at the cost that an incorrectly written cleanup function+-- cannot be interrupted.+--+-- For more information, please see <https://github.com/fpco/safe-exceptions/issues/3>.+-- -- @since 0.1.0.0 bracket :: MonadUnliftIO m => m a -> (a -> m b) -> (a -> m c) -> m c bracket before after thing = withRunInIO $ \run -> EUnsafe.mask $ \restore -> do@@ -295,13 +320,15 @@ _ <- EUnsafe.uninterruptibleMask_ $ run $ after x return y --- | Async safe version of 'EUnsafe.bracket_'.+-- | Same as 'bracket', but does not pass the acquired resource to cleanup and use functions. --+-- For more information, see @base@'s 'EUnsafe.bracket_'.+-- -- @since 0.1.0.0 bracket_ :: MonadUnliftIO m => m a -> m b -> m c -> m c bracket_ before after thing = bracket before (const after) (const thing) --- | Async safe version of 'EUnsafe.bracketOnError'.+-- | Same as 'bracket', but only perform the cleanup if an exception is thrown. -- -- @since 0.1.0.0 bracketOnError :: MonadUnliftIO m => m a -> (a -> m b) -> (a -> m c) -> m c@@ -323,10 +350,17 @@ bracketOnError_ :: MonadUnliftIO m => m a -> m b -> m c -> m c bracketOnError_ before after thing = bracketOnError before (const after) (const thing) --- | Async safe version of 'EUnsafe.finally'.+-- | Perform @thing@, guaranteeing that @after@ will run after, even if an exception occurs. --+-- Same interruptible vs uninterrupible points apply as with 'bracket'. See @base@'s+-- 'EUnsafe.finally' for more information.+-- -- @since 0.1.0.0-finally :: MonadUnliftIO m => m a -> m b -> m a+finally+ :: MonadUnliftIO m+ => m a -- ^ thing+ -> m b -- ^ after+ -> m a finally thing after = withRunInIO $ \run -> EUnsafe.uninterruptibleMask $ \restore -> do res1 <- EUnsafe.try $ restore $ run thing case res1 of@@ -353,13 +387,16 @@ EUnsafe.throwIO e1 Right x -> return x --- | Async safe version of 'EUnsafe.onException'.+-- | Like 'finally', but only call @after@ if an exception occurs. -- -- @since 0.1.0.0 onException :: MonadUnliftIO m => m a -> m b -> m a onException thing after = withException thing (\(_ :: SomeException) -> after) -- | Synchronously throw the given exception.+--+-- Note that, if you provide an exception value which is of an asynchronous+-- type, it will be wrapped up in 'SyncExceptionWrapper'. See 'toSyncException'. -- -- @since 0.1.0.0 throwIO :: (MonadIO m, Exception e) => e -> m a
src/UnliftIO/Internals/Async.hs view
@@ -383,8 +383,8 @@ -- | A more efficient alternative to 'Concurrently', which reduces the -- number of threads that need to be forked. For more information, see--- @FIXME link to blog post@. This is provided as a separate type to--- @Concurrently@ as it has a slightly different API.+-- [this blog post](https://www.fpcomplete.com/blog/transformations-on-applicative-concurrent-computations/).+-- This is provided as a separate type to @Concurrently@ as it has a slightly different API. -- -- Use the 'conc' function to construct values of type 'Conc', and -- 'runConc' to execute the composed actions. You can use the
unliftio.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: bc7612e266638a6d07a620579aafe7d2690c4bef6d120416a1a7a3fc8999b416+-- hash: c11215b81aa0898968a1dbf5189319f0bd07fa70861b5812ac34043c9f118ebb name: unliftio-version: 0.2.13+version: 0.2.13.1 synopsis: The MonadUnliftIO typeclass for unlifting monads to IO (batteries included) description: Please see the documentation and README at <https://www.stackage.org/package/unliftio> category: Control