packages feed

hoist-error 0.2.1.0 → 0.3.0.0

raw patch · 4 files changed

+241/−86 lines, 4 filesdep −eitherdep ~basedep ~mtlnew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies removed: either

Dependency ranges changed: base, mtl

API changes (from Hackage documentation)

- Control.Monad.Error.Hoist: class Monad m => HoistError m t e e' | t -> e
- Control.Monad.Error.Hoist: instance Control.Monad.Error.Class.MonadError e m => Control.Monad.Error.Hoist.HoistError m GHC.Base.Maybe () e
- Control.Monad.Error.Hoist: instance Control.Monad.Error.Class.MonadError e' m => Control.Monad.Error.Hoist.HoistError m (Control.Monad.Trans.Error.ErrorT e m) e e'
- Control.Monad.Error.Hoist: instance Control.Monad.Error.Class.MonadError e' m => Control.Monad.Error.Hoist.HoistError m (Data.Either.Either e) e e'
+ Control.Monad.Error.Hoist: class PluckError e t m | t -> e
+ Control.Monad.Error.Hoist: foldError :: (PluckError e t m, Monad m) => (e -> m r) -> (a -> m r) -> t a -> m r
+ Control.Monad.Error.Hoist: instance (GHC.Base.Applicative m, e GHC.Types.~ ()) => Control.Monad.Error.Hoist.PluckError e GHC.Maybe.Maybe m
+ Control.Monad.Error.Hoist: instance GHC.Base.Applicative m => Control.Monad.Error.Hoist.PluckError e (Data.Either.Either e) m
+ Control.Monad.Error.Hoist: instance GHC.Base.Monad m => Control.Monad.Error.Hoist.PluckError e (Control.Monad.Trans.Except.ExceptT e m) m
+ Control.Monad.Error.Hoist: pluckError :: (PluckError e t m, Applicative m) => t a -> m (Either e a)
+ Control.Monad.Fail.Hoist: (<!#>) :: (PluckError e t m, MonadFail m) => m (t a) -> String -> m a
+ Control.Monad.Fail.Hoist: (<#>) :: (PluckError e t m, MonadFail m) => t a -> String -> m a
+ Control.Monad.Fail.Hoist: (<%!#>) :: (PluckError e t m, MonadFail m) => m (t a) -> (e -> String) -> m a
+ Control.Monad.Fail.Hoist: (<%#>) :: (PluckError e t m, MonadFail m) => t a -> (e -> String) -> m a
+ Control.Monad.Fail.Hoist: hoistFail :: (PluckError e t m, MonadFail m) => (e -> String) -> t a -> m a
+ Control.Monad.Fail.Hoist: hoistFail' :: (PluckError String t m, MonadFail m) => t a -> m a
+ Control.Monad.Fail.Hoist: hoistFailM :: (PluckError e t m, MonadFail m) => (e -> String) -> m (t a) -> m a
+ Control.Monad.Fail.Hoist: hoistFailM' :: (PluckError String t m, MonadFail m) => m (t a) -> m a
+ Control.Monad.Fail.Hoist: infixl 8 <!#>
- Control.Monad.Error.Hoist: (<!?>) :: HoistError m t e e' => m (t a) -> e' -> m a
+ Control.Monad.Error.Hoist: (<!?>) :: (PluckError e t m, MonadError e' m) => m (t a) -> e' -> m a
- Control.Monad.Error.Hoist: (<%!?>) :: HoistError m t e e' => m (t a) -> (e -> e') -> m a
+ Control.Monad.Error.Hoist: (<%!?>) :: (PluckError e t m, MonadError e' m) => m (t a) -> (e -> e') -> m a
- Control.Monad.Error.Hoist: (<%?>) :: HoistError m t e e' => t a -> (e -> e') -> m a
+ Control.Monad.Error.Hoist: (<%?>) :: (PluckError e t m, MonadError e' m) => t a -> (e -> e') -> m a
- Control.Monad.Error.Hoist: (<?>) :: HoistError m t e e' => t a -> e' -> m a
+ Control.Monad.Error.Hoist: (<?>) :: (PluckError e t m, MonadError e' m) => t a -> e' -> m a
- Control.Monad.Error.Hoist: hoistError :: HoistError m t e e' => (e -> e') -> t a -> m a
+ Control.Monad.Error.Hoist: hoistError :: (PluckError e t m, MonadError e' m) => (e -> e') -> t a -> m a
- Control.Monad.Error.Hoist: hoistErrorM :: HoistError m t e e' => (e -> e') -> m (t a) -> m a
+ Control.Monad.Error.Hoist: hoistErrorM :: (PluckError e t m, MonadError e' m) => (e -> e') -> m (t a) -> m a

Files

changelog.md view
@@ -1,3 +1,14 @@+0.3.0.0++* Replaced the `HoistError` typeclass, which is about monads, with a+  simpler `PluckError` typeclass that is about extracting errors from+  values.+* Removed the instance for `Except e`, as it triggers overlapping+  instance errors when attempting to hoist `ExceptT e m` for unknown+  monads `m`.+* Introduced a parallel `Control.Monad.Fail.Hoist` module, for+  hoisting error messages into `MonadFail`.+ 0.2.1.0  * Removed unicode syntax and variables
hoist-error.cabal view
@@ -1,5 +1,5 @@ name:                hoist-error-version:             0.2.1.0+version:             0.3.0.0 synopsis:            Some convenience facilities for hoisting errors into a monad description:         Provides a typeclass and useful combinators for hoisting errors into a monad. license:             MIT@@ -10,27 +10,27 @@ category:            Control build-type:          Simple cabal-version:       >=1.10-tested-with:         GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1  extra-source-files:  changelog.md -tested-with:         GHC == 7.8.4-                   , GHC == 7.10.3-                   , GHC == 8.0.2-                   , GHC == 8.2.2-                   , GHC == 8.4.1--+tested-with:         GHC == 8.8.4+                   , GHC == 8.10.7+                   , GHC == 9.0.1+                   , GHC == 9.2.7+                   , GHC == 9.4.5+                   , GHC == 9.6.4+                   , GHC == 9.8.2  source-repository head     type: git     location: https://github.com/alephcloud/hs-hoist-error.git  library-  exposed-modules:     Control.Monad.Error.Hoist-  build-depends:       base   >=4.7 && <4.12-                     , mtl    >=2.1 && <2.3-                     , either >=4   && <6+  exposed-modules:+    Control.Monad.Error.Hoist+    Control.Monad.Fail.Hoist+  build-depends:       base   >=4.13 && <4.20+                     , mtl    >=2.2 && <2.4    hs-source-dirs:      src   default-language:    Haskell2010
src/Control/Monad/Error/Hoist.hs view
@@ -1,44 +1,50 @@-{-# LANGUAGE CPP                    #-} {-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE DefaultSignatures      #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs                  #-}-{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE UndecidableInstances   #-} --- | 'HoistError' extends 'MonadError' with 'hoistError', which enables lifting--- of partiality types such as 'Maybe' and @'Either' e@ into the monad.+-- | This module provides helper functions for lifting partiality types into+-- error-carrying monads like 'ExceptT'. -- -- For example, consider the following @App@ monad that may throw @BadPacket@ -- errors: -- -- @--- data AppError = BadPacket 'String'+-- data AppError = BadPacket 'Text' -- -- newtype App a = App ('EitherT' AppError 'IO') a --  deriving ('Functor', 'Applicative', 'Monad', 'MonadError' AppError, 'MonadIO') -- @ ----- We may have an existing function that parses a 'String' into a @'Maybe' Packet@+-- We may have an existing function that attempts to parse a 'ByteString': -- -- @--- parsePacket :: 'String' -> 'Maybe' Packet+-- parsePacket :: 'ByteString' -> 'Either' 'Text' Packet -- @ ----- which can be lifted into the @App@ monad with 'hoistError'+-- We can lift this error into the @App@ monad using @('<%?>')@: -- -- @--- appParsePacket :: 'String' -> 'App' Packet--- appParsePacket s = 'hoistError' (\\() -> BadPacket "no parse") (parsePacket s)+-- appParsePacket :: 'ByteString' -> 'App' Packet+-- appParsePacket s = parsePacket s \<%?\> BadPacket -- @ ----- Similar instances exist for @'Either' e@ and @'EitherT' e m@.+-- Instances also exist for extracting errors from other partiality types+-- like @'Either' e@ and @'ExceptT' e m@.  module Control.Monad.Error.Hoist-  ( HoistError(..)+  ( hoistError   , hoistErrorM+  -- ** Operators+  -- $mnemonics   , (<%?>)   , (<%!?>)   , (<?>)   , (<!?>)+  -- * Helper class+  , PluckError(..)   ) where  import           Control.Monad              ((<=<))@@ -46,63 +52,23 @@  import           Data.Either                (Either, either) -#if MIN_VERSION_mtl(2,2,2) import           Control.Monad.Except       (Except, ExceptT, runExcept,                                              runExceptT)-#else-import           Control.Monad.Error        (Error, ErrorT, runErrorT)-#endif -#if MIN_VERSION_either(5,0,0)--- Control.Monad.Trans.Either was removed from @either@ in version 5.-#else-import           Control.Monad.Trans.Either (EitherT, eitherT, runEitherT)-#endif---- | A tricky class for easily hoisting errors out of partiality types (e.g.--- 'Maybe', @'Either' e@) into a monad. The parameter @e@ represents the error--- information carried by the partiality type @t@, and @e'@ represents the type--- of error expected in the monad @m@.+-- | Given a conversion from the error in @t a@ to @e'@, we can hoist the+-- computation into @m@. ---class Monad m => HoistError m t e e' | t -> e where--  -- | Given a conversion from the error in @t a@ to @e'@, we can hoist the-  -- computation into @m@.-  ---  -- @-  -- 'hoistError' :: 'MonadError' e m -> (() -> e) -> 'Maybe'       a -> m a-  -- 'hoistError' :: 'MonadError' e m -> (a  -> e) -> 'Either'  a   b -> m b-  -- 'hoistError' :: 'MonadError' e m -> (a  -> e) -> 'ExceptT' a m b -> m b-  -- @-  hoistError-    :: (e -> e')-    -> t a-    -> m a--instance MonadError e m => HoistError m Maybe () e where-  hoistError f = maybe (throwError $ f ()) return--instance MonadError e' m => HoistError m (Either e) e e' where-  hoistError f = either (throwError . f) return--#if MIN_VERSION_either(5,0,0)--- Control.Monad.Trans.Either was removed from @either@ in version 5.-#else-instance (m ~ n, MonadError e' m) => HoistError m (EitherT e n) e e' where-  hoistError f = eitherT (throwError . f) return-#endif--#if MIN_VERSION_mtl(2,2,2)-instance MonadError e' m => HoistError m (Except e) e e' where-  hoistError f = either (throwError . f) return . runExcept--instance MonadError e' m => HoistError m (ExceptT e m) e e' where-  hoistError f = either (throwError . f) return <=< runExceptT-#else--- 'ErrorT' was renamed to 'ExceptT' in mtl 2.2.2-instance MonadError e' m => HoistError m (ErrorT e m) e e' where-  hoistError f = either (throwError . f) return <=< runErrorT-#endif+-- @+-- 'hoistError' :: 'MonadError' e m -> (() -> e) -> 'Maybe'       a -> m a+-- 'hoistError' :: 'MonadError' e m -> (a  -> e) -> 'Either'  a   b -> m b+-- 'hoistError' :: 'MonadError' e m -> (a  -> e) -> 'ExceptT' a m b -> m b+-- @+hoistError+  :: (PluckError e t m, MonadError e' m)+  => (e -> e')+  -> t a+  -> m a+hoistError f = foldError (throwError . f) pure  -- | A version of 'hoistError' that operates on values already in the monad. --@@ -112,14 +78,28 @@ -- 'hoistErrorM' :: 'MonadError' e m => (a  -> e) ->    'ExceptT' a m b  -> 'ExceptT' a m b -- @ hoistErrorM-  :: HoistError m t e e'+  :: (PluckError e t m, MonadError e' m)   => (e -> e')   -> m (t a)   -> m a-hoistErrorM e m = do-  x <- m-  hoistError e x+hoistErrorM e m = m >>= hoistError e +-- $mnemonics+--+-- The operators in this package are named according to a scheme:+--+-- * @('<?>')@ is the simplest error-handling function: it replaces+--   any error with its second argument.+--+-- * The additional @!@ in @('<!?>')@ and @('<%!?>')@ means the+--   operator handles values that are already "in a monad".+--+-- * The additional @%@ in @('<%?>')@ and @('<%!?>')@ means the+--   operator takes a function argument, which it applies to the error+--   from the partiality type. (The mnemonic is that @%@ sometimes+--   means "mod", and we abuse "mod" as a shorthand for "modify". It's a+--   long bow, but @lens@ uses the same mnemonic.)+ -- | A flipped synonym for 'hoistError'. -- -- @@@ -128,7 +108,7 @@ -- ('<%?>') :: 'MonadError' e m => 'ExceptT' a m b -> (a  -> e) -> 'ExceptT' a m b -- @ (<%?>)-  :: HoistError m t e e'+  :: (PluckError e t m, MonadError e' m)   => t a   -> (e -> e')   -> m a@@ -145,7 +125,7 @@ -- ('<%!?>') :: 'MonadError' e m =>    'ExceptT' a m b  -> (a  -> e) -> 'ExceptT' a m b -- @ (<%!?>)-  :: HoistError m t e e'+  :: (PluckError e t m, MonadError e' m)   => m (t a)   -> (e -> e')   -> m a@@ -163,7 +143,7 @@ -- ('<?>') :: 'MonadError' e m => 'ExceptT' a m b -> e -> 'ExceptT' a m b -- @ (<?>)-  :: HoistError m t e e'+  :: (PluckError e t m, MonadError e' m)   => t a   -> e'   -> m a@@ -180,7 +160,7 @@ -- ('<!?>') :: 'MonadError' e m =>    'ExceptT' a m b  -> e -> 'ExceptT' a m b -- @ (<!?>)-  :: HoistError m t e e'+  :: (PluckError e t m, MonadError e' m)   => m (t a)   -> e'   -> m a@@ -190,3 +170,27 @@  infixl 8 <!?> {-# INLINE (<!?>) #-}++-- | A class for plucking an error @e@ out of a partiality type @t@.+class PluckError e t m | t -> e where+  pluckError :: t a -> m (Either e a)+  default pluckError :: Applicative m => t a -> m (Either e a)+  pluckError = foldError (pure . Left) (pure . Right)++  foldError :: (e -> m r) -> (a -> m r) -> t a -> m r+  default foldError :: Monad m => (e -> m r) -> (a -> m r) -> t a -> m r+  foldError f g = either f g <=< pluckError++  {-# MINIMAL pluckError | foldError #-}++instance (Applicative m, e ~ ()) => PluckError e Maybe m where+  pluckError = pure . maybe (Left ()) Right+  foldError f = maybe (f ())++instance Applicative m => PluckError e (Either e) m where+  pluckError = pure+  foldError = either++instance Monad m => PluckError e (ExceptT e m) m where+  pluckError = runExceptT+  foldError f g = either f g <=< runExceptT
+ src/Control/Monad/Fail/Hoist.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE FlexibleContexts #-}++-- | This module provides helpers for converting partiality types into+-- 'MonadFail' computations.+--+-- 'MonadFail''s purpose is to handle pattern-match failures in+-- @do@-expressions, and not to be a general-purpose error-handling+-- mechanism. Despite this, some libraries use it as one, and this+-- module can help you report errors via 'MonadFail'.+--+-- The operator mnemonics are the same as in+-- "Control.Monad.Error.Hoist", but with @#@ in place of @?@. You can+-- imagine a hastily-written @F@ looking kinda-sorta like a @#@, if it+-- helps.++module Control.Monad.Fail.Hoist+  ( hoistFail+  , hoistFail'+  , hoistFailM+  , hoistFailM'+  -- ** Operators+  , (<%#>)+  , (<%!#>)+  , (<#>)+  , (<!#>)+  ) where++import           Control.Monad.Error.Hoist  (PluckError(..))++-- | Given a conversion from the error in @t a@ to @String@, we can hoist the+-- computation into @m@.+--+-- @+-- 'hoistFail' :: 'MonadFail' m => (() -> String) -> 'Maybe'    a -> m a+-- 'hoistFail' :: 'MonadFail' m => (a  -> String) -> 'Either' a b -> m b+-- @+hoistFail+  :: (PluckError e t m, MonadFail m)+  => (e -> String)+  -> t a+  -> m a+hoistFail f = foldError (fail . f) pure++-- | Hoist computations whose error type is already 'String'.+hoistFail' :: (PluckError String t m, MonadFail m) => t a -> m a+hoistFail' = hoistFail id++-- | A version of 'hoistFail' that operates on values already in the monad.+--+-- @+-- 'hoistFailM' :: 'MonadFail' m => (() -> String) -> m ('Maybe'       a) ->           m a+-- 'hoistFailM' :: 'MonadFail' m => (a  -> String) -> m ('Either'  a   b) ->           m b+-- 'hoistFailM' :: 'MonadFail' m => (a  -> String) ->    'ExceptT' a m b  -> 'ExceptT' a m b+-- @+hoistFailM+  :: (PluckError e t m, MonadFail m)+  => (e -> String)+  -> m (t a)+  -> m a+hoistFailM f m = m >>= hoistFail f++-- | A version of 'hoistFail'' that operates on values already in the monad.+--+-- @+-- 'hoistFailM'' :: 'MonadFail' m => m ('Maybe'       a) ->           m a+-- 'hoistFailM'' :: 'MonadFail' m => m ('Either'  a   b) ->           m b+-- 'hoistFailM'' :: 'MonadFail' m =>    'ExceptT' a m b  -> 'ExceptT' a m b+-- @+hoistFailM'+  :: (PluckError String t m, MonadFail m)+  => m (t a)+  -> m a+hoistFailM' = hoistFailM id++-- | A flipped synonym for 'hoistFail'. Mnemonic: @#@ looks a bit like @F@.+--+-- @+-- ('<%#>') :: 'MonadFail' m => 'Maybe'    a -> (() -> e) -> m a+-- ('<%#>') :: 'MonadFail' m => 'Either' a b -> (a  -> e) -> m b+-- @+(<%#>)+  :: (PluckError e t m, MonadFail m)+  => t a+  -> (e -> String)+  -> m a+(<%#>) = flip hoistFail++infixl 8 <%#>+{-# INLINE (<%#>) #-}++-- | A flipped synonym for 'hoistFailM'.+--+-- @+-- ('<%!#>') :: 'MonadError' e m => m ('Maybe'       a) -> (() -> e) ->           m a+-- ('<%!#>') :: 'MonadError' e m => m ('Either'  a   b) -> (a  -> e) ->           m b+-- ('<%!#>') :: 'MonadError' e m =>    'ExceptT' a m b  -> (a  -> e) -> 'ExceptT' a m b+-- @+(<%!#>)+  :: (PluckError e t m, MonadFail m)+  => m (t a)+  -> (e -> String)+  -> m a+(<%!#>) = flip hoistFailM++infixl 8 <%!#>+{-# INLINE (<%!#>) #-}++-- | A version of '<%#>' that ignores the error in @t a@ and fails+-- with a new one.+--+-- @+-- ('<#>') :: 'MonadFail' m => 'Maybe'    a -> String -> m a+-- ('<#>') :: 'MonadFail' m => 'Either' a b -> String -> m b+-- @+(<#>)+  :: (PluckError e t m, MonadFail m)+  => t a+  -> String+  -> m a+m <#> e = m <%#> const e++infixl 8 <#>+{-# INLINE (<#>) #-}++-- | A version of '<#>' that operates on values already in the monad.+--+-- @+-- ('<!#>') :: 'MonadFail m => m ('Maybe'       a) -> String ->           m a+-- ('<!#>') :: 'MonadFail m => m ('Either'  a   b) -> String ->           m b+-- ('<!#>') :: 'MonadFail m =>    'ExceptT' a m b  -> String -> 'ExceptT' a m b+-- @+(<!#>)+  :: (PluckError e t m, MonadFail m)+  => m (t a)+  -> String+  -> m a+m <!#> e = m >>= hoistFail (const e)++infixl 8 <!#>+{-# INLINE (<!#>) #-}