| Copyright | (c) Dennis Gosnell 2016 |
|---|---|
| License | BSD-style (see LICENSE file) |
| Maintainer | cdep.illabout@gmail.com |
| Stability | experimental |
| Portability | POSIX |
| Safe Haskell | Safe |
| Language | Haskell2010 |
Control.FromSum
Contents
Synopsis
- fromEitherM :: Applicative m => (e -> m a) -> Either e a -> m a
- fromEitherOrM :: Applicative m => Either e a -> (e -> m a) -> m a
- fromEitherM_ :: (Applicative m, Monoid b) => (e -> m b) -> Either e a -> m b
- fromEitherOrM_ :: (Applicative m, Monoid b) => Either e a -> (e -> m b) -> m b
- fromMaybeM :: Applicative m => m a -> Maybe a -> m a
- fromMaybeOrM :: Applicative m => Maybe a -> m a -> m a
- fromMaybeM_ :: (Applicative m, Monoid b) => m b -> Maybe a -> m b
- fromMaybeOrM_ :: (Applicative m, Monoid b) => Maybe a -> m b -> m b
- fromEitherMM :: Monad m => (e -> m a) -> m (Either e a) -> m a
- fromEitherOrMM :: Monad m => m (Either e a) -> (e -> m a) -> m a
- fromMaybeMM :: Monad m => m a -> m (Maybe a) -> m a
- fromMaybeOrMM :: Monad m => m (Maybe a) -> m a -> m a
- fromEither :: (e -> a) -> Either e a -> a
- fromEitherOr :: Either e a -> (e -> a) -> a
- fromMaybe :: a -> Maybe a -> a
- fromMaybeOr :: Maybe a -> a -> a
- maybeToEither :: e -> Maybe a -> Either e a
- maybeToEitherOr :: Maybe a -> e -> Either e a
- eitherToMaybe :: Either e a -> Maybe a
- collapseEither :: Either a a -> a
- collapseExceptT :: Monad m => ExceptT a m a -> m a
- collapseErrExceptT :: Monad m => ExceptT (m a) m a -> m a
- liftEitherExceptT :: Applicative m => Either e a -> ExceptT e m a
- fromEitherExceptT :: Monad m => (e -> x) -> Either e a -> ExceptT x m a
- fromEitherOrExceptT :: Monad m => Either e a -> (e -> x) -> ExceptT x m a
- fromEitherMExceptT :: Monad m => (e -> x) -> m (Either e a) -> ExceptT x m a
- fromEitherOrMExceptT :: Monad m => m (Either e a) -> (e -> x) -> ExceptT x m a
- fromMaybeExceptT :: Monad m => x -> Maybe a -> ExceptT x m a
- fromMaybeOrExceptT :: Monad m => Maybe a -> x -> ExceptT x m a
- fromMaybeMExceptT :: Monad m => x -> m (Maybe a) -> ExceptT x m a
- fromMaybeOrMExceptT :: Monad m => m (Maybe a) -> x -> ExceptT x m a
- guardExceptT :: Monad m => Bool -> x -> ExceptT x m ()
- guardMExceptT :: Monad m => m Bool -> x -> ExceptT x m ()
Monadic in return value
fromEitherM :: Applicative m => (e -> m a) -> Either e a -> m a Source #
A monadic version of fromEither.
fromEitherMleftAction ===eitherleftActionpure
>>>fromEitherM (\s -> [length s]) $ Right 5[5]>>>fromEitherM (\s -> [length s]) $ Left ("foo" :: String)[3]
fromEitherOrM :: Applicative m => Either e a -> (e -> m a) -> m a Source #
A fliped version of fromEitherM.
>>>fromEitherOrM (Right 5) $ \s -> [length s][5]
This can be nice to use as an error handler.
>>>fromEitherOrM (Right 5) $ \s -> putStrLn ("error: " ++ s) >> undefined5>>>fromEitherOrM (Left "foo") $ \s -> putStrLn ("error: " ++ s) >> undefinederror: foo ...
fromEitherM_ :: (Applicative m, Monoid b) => (e -> m b) -> Either e a -> m b Source #
Similar to fromEitherM, but only run the monadic leftAction if the
Either argument is Left. Otherwise, return pure mempty.
fromEitherM_leftAction ===eitherleftAction (const$puremempty)
>>>fromEitherM_ (\err -> putStrLn err >> pure "bye") $ Right 5"">>>fromEitherM_ (\err -> putStrLn err >> pure "bye") $ Left "there was an error"there was an error "bye"
This can be convenient when you want to run some sort of logging function
whenever an Either is Left. If you imagine the logging function is
b -> , then the effective type of IO '()'fromEitherM_ becomes
, because
'()' has a fromEitherM_ :: (e -> IO '()') -> Either e a -> IO '()'Monoid instance, and IO, has an Applicative instance.
>>>fromEitherM_ putStrLn $ Left "there was an error"there was an error
fromEitherOrM_ :: (Applicative m, Monoid b) => Either e a -> (e -> m b) -> m b Source #
A fliped version of fromEitherM_.
fromMaybeM :: Applicative m => m a -> Maybe a -> m a Source #
A monadic version of fromMaybe.
fromMaybeMnothingAction ===maybenothingActionpure
>>>fromMaybeM [] $ Just 5[5]>>>fromMaybeM [] Nothing[]
fromMaybeOrM :: Applicative m => Maybe a -> m a -> m a Source #
A fliped version of fromMaybeM.
>>>fromMaybeOrM (Just 5) [][5]
This can be nice to use as an error handler.
>>>fromMaybeOrM (Just 5) $ putStrLn "some error occurred" >> undefined5>>>fromMaybeOrM (Nothing) $ putStrLn "some error occurred" >> undefinedsome error occurred ...
fromMaybeM_ :: (Applicative m, Monoid b) => m b -> Maybe a -> m b Source #
Similar to fromMaybeM, but only run the monadic nothingAction if the
Maybe argument is Nothing. Otherwise, return pure mempty.
fromMaybeM_nothingAction ===maybenothingAction (const$puremempty)
>>>fromMaybeM_ (putStrLn "hello" >> pure "bye") $ Just 5"">>>fromMaybeM_ (putStrLn "hello" >> pure "bye") Nothinghello "bye"
This can be convenient when you want to run some sort of logging function
whenever a Maybe is Nothing. If you imagine the logging function is
, then the effective type of IO '()'fromMaybeM_ becomes
, because '()' has a
fromMaybeM_ :: IO '()' -> Maybe a -> IO '()'Monoid instance, and IO, has an Applicative instance.
>>>fromMaybeM_ (putStrLn "hello") Nothinghello
fromMaybeOrM_ :: (Applicative m, Monoid b) => Maybe a -> m b -> m b Source #
A fliped version of fromMaybeM.
Monadic in both return and sum-type value
fromEitherMM :: Monad m => (e -> m a) -> m (Either e a) -> m a Source #
Similar to fromEitherM but the Either argument is also a monadic value.
>>>fromEitherMM (\s -> [length s]) [Right 5, Right 10][5,10]>>>fromEitherMM (\s -> [length s]) [Left ("foo" :: String), Right 100][3,100]
NOTE: I don't particularly like the name of this function. If you have a suggestion for a better name, please submit a PR or issue.
fromEitherOrMM :: Monad m => m (Either e a) -> (e -> m a) -> m a Source #
A fliped version of fromEitherMM.
fromMaybeMM :: Monad m => m a -> m (Maybe a) -> m a Source #
Similar to fromMaybeM but the Maybe argument is also a monadic value.
>>>fromMaybeMM [] [Just 6, Just 5][6,5]>>>fromMaybeMM [] [Just 6, Nothing, Just 7][6,7]
NOTE: I don't particularly like the name of this function. If you have a suggestion for a better name, please submit a PR or issue.
fromMaybeOrMM :: Monad m => m (Maybe a) -> m a -> m a Source #
A fliped version of fromMaybeMM.
Completely non-monadic functions
fromEither :: (e -> a) -> Either e a -> a Source #
Similar to fromMaybe.
>>>fromEither show $ Left 5"5">>>fromEither show $ Right "hello""hello"
fromEitherOr :: Either e a -> (e -> a) -> a Source #
A fliped version of fromEither.
fromMaybe :: a -> Maybe a -> a #
The fromMaybe function takes a default value and and Maybe
value. If the Maybe is Nothing, it returns the default values;
otherwise, it returns the value contained in the Maybe.
Examples
Basic usage:
>>>fromMaybe "" (Just "Hello, World!")"Hello, World!"
>>>fromMaybe "" Nothing""
Read an integer from a string using readMaybe. If we fail to
parse an integer, we want to return 0 by default:
>>>import Text.Read ( readMaybe )>>>fromMaybe 0 (readMaybe "5")5>>>fromMaybe 0 (readMaybe "")0
Converting from Maybe to Either
maybeToEither :: e -> Maybe a -> Either e a Source #
maybeToEitherOr :: Maybe a -> e -> Either e a Source #
A fliped version of maybeToEither.
>>>maybeToEitherOr (Just "hello") 3Right "hello"
>>>maybeToEitherOr Nothing 3Left 3
eitherToMaybe :: Either e a -> Maybe a Source #
Collapsing funtions
collapseEither :: Either a a -> a Source #
Collapse an to an Either a aa. Defined as .fromEither id
Note: Other libraries export this function as fromEither, but our
fromEither function is slightly more general.
>>>collapseEither (Right 3)3>>>collapseEither (Left "hello")"hello"
collapseExceptT :: Monad m => ExceptT a m a -> m a Source #
Similar to collapseEither, but for ExceptT.
>>>collapseExceptT (ExceptT $ pure (Right 3))3>>>collapseExceptT (ExceptT $ pure (Left "hello"))"hello"
collapseErrExceptT :: Monad m => ExceptT (m a) m a -> m a Source #
Collapse an ExceptT where the error returns the same type as the whole
computation.
>>>let exceptTOne = pure 3 :: ExceptT (IO Int) IO Int>>>collapseErrExceptT exceptTOne :: IO Int3
This is helpful when writing short-circuiting computations where you throw errors that match the type of the underlying computation.
>>>:{let go :: Int -> ExceptT (IO ()) IO () go x = do bar <- if x < 10 then pure "hello" else throwE (putStrLn "Error occurred, x too big!") lift $ putStrLn $ bar ++ " world" :}
>>>collapseErrExceptT (go 100) :: IO ()Error occurred, x too big!>>>collapseErrExceptT (go 3) :: IO ()hello world
In this example, the error type in the ExceptT is .
This allows us to easily short-circuit the remaining computations.
In this example, the remaining computation is just printing
IO ()bar .++ " world"
Converting to ExceptT
liftEitherExceptT :: Applicative m => Either e a -> ExceptT e m a Source #
Lift an Either into an ExceptT.
This is the same as liftEither, but the return type
is specialized for ExceptT.
>>>liftEitherExceptT (Right 3) :: ExceptT String Identity IntExceptT (Identity (Right 3))
Note that if you want to lift m ( to Either e a),
just use ExceptT e m aExceptT:
>>>action = Identity (Left "error") :: Identity (Either String Int)>>>ExceptT action :: ExceptT String Identity IntExceptT (Identity (Left "error"))
fromEitherExceptT :: Monad m => (e -> x) -> Either e a -> ExceptT x m a Source #
Lift an Either to an ExceptT with a handler for transforming the error
value.
If the input Either is Right, then just return it like normal:
>>>let rightEither = Right () :: Either String ()>>>fromEitherExceptT (\str -> length str) rightEither :: ExceptT Int Identity ()ExceptT (Identity (Right ()))
If the input Either is Left, then pass the value to the handler:
>>>let leftEither = Left "hello" :: Either String ()>>>fromEitherExceptT (\str -> length str) leftEither :: ExceptT Int Identity ()ExceptT (Identity (Left 5))
fromEitherOrExceptT :: Monad m => Either e a -> (e -> x) -> ExceptT x m a Source #
Just like fromEitherExceptT, but the arguments are flipped.
fromEitherMExceptT :: Monad m => (e -> x) -> m (Either e a) -> ExceptT x m a Source #
Similar to fromEitherExceptT but the Either value is lifted in a
Monad.
>>>let identityLeft = Identity (Left "hello") :: Identity (Either String ())>>>fromEitherMExceptT (\str -> length str) identityLeft :: ExceptT Int Identity ()ExceptT (Identity (Left 5))
This is similar to withExceptT, but the second
argument is the unwrapped ExceptT computation.
fromEitherOrMExceptT :: Monad m => m (Either e a) -> (e -> x) -> ExceptT x m a Source #
Just like fromEitherOrMExceptT, but the arguments are flipped.
fromMaybeExceptT :: Monad m => x -> Maybe a -> ExceptT x m a Source #
Lift a Maybe to an ExceptT with a default value for the case when
the Maybe is Nothing.
If the Maybe is Just, then just return the value like normal:
>>>let justVal = Just True :: Maybe Bool>>>fromMaybeExceptT 5 justVal :: ExceptT Int Identity BoolExceptT (Identity (Right True))
If the Maybe is Nothing, then use the default value as the error value:
>>>let nothingVal = Nothing :: Maybe Bool>>>fromMaybeExceptT 5 nothingVal :: ExceptT Int Identity BoolExceptT (Identity (Left 5))
fromMaybeOrExceptT :: Monad m => Maybe a -> x -> ExceptT x m a Source #
Just like fromMaybeExceptT but with the arguments flipped.
fromMaybeMExceptT :: Monad m => x -> m (Maybe a) -> ExceptT x m a Source #
Similar to fromMaybeExceptT except the Maybe value is lifted in a
Monad.
>>>let identityNothing = Identity Nothing :: Identity (Maybe Bool)>>>fromMaybeMExceptT 5 identityNothing :: ExceptT Int Identity BoolExceptT (Identity (Left 5))
fromMaybeOrMExceptT :: Monad m => m (Maybe a) -> x -> ExceptT x m a Source #
Just like fromMaybeMExceptT but with the arguments flipped.
guardExceptT :: Monad m => Bool -> x -> ExceptT x m () Source #
Similar to guard, but for ExceptT.
If the Bool is True, then do nothing.
>>>guardExceptT True "error occurred" :: ExceptT String Identity ()ExceptT (Identity (Right ()))
If the Bool is False, then return the error case:
>>>guardExceptT False "error occurred" :: ExceptT String Identity ()ExceptT (Identity (Left "error occurred"))
guardMExceptT :: Monad m => m Bool -> x -> ExceptT x m () Source #
Just like guardExceptT (and similar to guardM), except the boolean is
lifted in a Monad.
>>>guardMExceptT (Identity False) "error occurred" :: ExceptT String Identity ()ExceptT (Identity (Left "error occurred"))
Example converting to ExceptT
Functions like fromMaybeExceptT and fromEitherExceptT are convenient
when paired with collapseErrExceptT. This section explains how
you can use these functions together.
Imagine you're writing a function that pulls user names from a database, reads the first character of the name, and prints it to the console. The functions for reading names from a database, and for parsing the first character of the name could fail, so we will handle these errors by logging to the console.
Here's the function we will be using for pulling user names from
the database. If we pass 0, it returns "SPJ". If we pass 1,
it returns an empty string. Otherwise it returns Nothing:
>>>:{let getUserNameFromDb :: Int -> Maybe String getUserNameFromDb 0 = Just "SPJ" getUserNameFromDb 1 = Just "" getUserNameFromDb _ = Nothing :}
Here's the function we will be using for parsing the first character
of a user name. If the user name is an empty string, we return
Left with an error message. Otherwise we return the first
character of the user name:
>>>:{let parseFirstCharFromName :: String -> Either String Char parseFirstCharFromName [] = Left "user name is empty" parseFirstCharFromName (h:_) = Right h :}
Now let's write our function. If you didn't have the combinators from above
like fromEitherExceptT and collapseErrExceptT, you might be tempted to
write nested case patterns:
>>>:{let nestedPrintFirstCharOfUserName :: Int -> IO () nestedPrintFirstCharOfUserName i = -- Try to get the username for id i. case getUserNameFromDb i of -- If we couldn't get the user name from the database -- print an error to the console. Nothing -> putStrLn $ "ERROR: couldn't get user name for user " ++ show i Just name -> -- Try to parse the first character of the user name. case parseFirstCharFromName name of -- If we couldn't parse the first character of the user name, -- print an error to the console. Left err -> putStrLn $ "ERROR: " ++ err Right firstChar -> -- Print the first character of the user name to the console. putStrLn $ "Got first character of name for id " ++ show i ++ ": " ++ [firstChar] :}
Here's an example of using this function, including the error cases:
>>>nestedPrintFirstCharOfUserName 100ERROR: couldn't get user name for user 100>>>nestedPrintFirstCharOfUserName 1ERROR: user name is empty>>>nestedPrintFirstCharOfUserName 0Got first character of name for id 0: S
This works, and is understandable, but it gets unwieldy when there are even more parsing steps. You can get very deeply nested cases.
In order to write this without deeply nested error handling, you need a
short-circuiting Monad. Two popular examples are MaybeT and ExceptT.
Using collapseErrExceptT (and ExcepT), it is possible to write this
function by short-circuiting on errors:
>>>:{let printFirstCharOfUserName :: Int -> IO () printFirstCharOfUserName i = -- The argument to collapseErrExceptT is @ExceptT (IO ()) IO ()@. -- This can be thought of as an action that can short-circuit -- with @IO ()@ error-handling actions. -- -- The error-handling actions below just log to the console. collapseErrExceptT $ do -- Get the user name from the db. -- If getUserNameFromDb returns Nothing, then this whole -- block will short circuit and collapseErrExceptT will -- run our error handler. -- -- Note that for the type of the error handler to work -- correctly with @collapseErrExceptT@, the error -- handler has to return @IO ()@. name <- fromMaybeOrExceptT (getUserNameFromDb i) $ putStrLn $ "ERROR: couldn't get user name for user " ++ show i -- Parse out the first character from the user name. -- If parseFirstCharFromName returns Left, then this whole -- block will short circuit and collapseErrExceptT will -- run our error handler. firstChar <- fromEitherOrExceptT (parseFirstCharFromName name) $ \err -> putStrLn $ "ERROR: " ++ err -- Print the first character of the name. -- This needs to be 'lift'ed because this whole block is -- actually @ExceptT (IO ()) IO ()@. lift $ putStrLn $ "Got first character of name for id " ++ show i ++ ": " ++ [firstChar] :}
The main good point here is that using the short-circuiting functionality of
ExceptT, we can write everything without nesting.
Here's a few examples of calling printFirstCharOfUserName.
Here we pass a user id that doesn't exist, so getUserNameFromDb will
return Nothing. This causes the function to short-circuit and the
first error handler to be called.
>>>printFirstCharOfUserName 100ERROR: couldn't get user name for user 100
Here we pass a user id that does exist, but the user name for this user id is empty. This causes the function to short-circuit and the second error handler to be called.
>>>printFirstCharOfUserName 1ERROR: user name is empty
This time the function succeeds:
>>>printFirstCharOfUserName 0Got first character of name for id 0: S
In real code, the functions getUserNameFromDb and parseFirstCharFromName
will have monadic return values. In that case, you can use
fromMaybeOrMExceptT and fromEitherOrMExceptT.
The following is setup code for doctests in this module.
>>>import Data.Functor.Identity (Identity(Identity))