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 |
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
.
fromEitherM
leftAction ===either
leftActionpure
>>>
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 flip
ed 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) >> undefined
5>>>
fromEitherOrM (Left "foo") $ \s -> putStrLn ("error: " ++ s) >> undefined
error: 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 ===either
leftAction (const
$
pure
mempty
)
>>>
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 flip
ed version of fromEitherM_
.
fromMaybeM :: Applicative m => m a -> Maybe a -> m a Source #
A monadic version of fromMaybe
.
fromMaybeM
nothingAction ===maybe
nothingActionpure
>>>
fromMaybeM [] $ Just 5
[5]>>>
fromMaybeM [] Nothing
[]
fromMaybeOrM :: Applicative m => Maybe a -> m a -> m a Source #
A flip
ed version of fromMaybeM
.
>>>
fromMaybeOrM (Just 5) []
[5]
This can be nice to use as an error handler.
>>>
fromMaybeOrM (Just 5) $ putStrLn "some error occurred" >> undefined
5>>>
fromMaybeOrM (Nothing) $ putStrLn "some error occurred" >> undefined
some 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 ===maybe
nothingAction (const
$
pure
mempty
)
>>>
fromMaybeM_ (putStrLn "hello" >> pure "bye") $ Just 5
"">>>
fromMaybeM_ (putStrLn "hello" >> pure "bye") Nothing
hello "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") Nothing
hello
fromMaybeOrM_ :: (Applicative m, Monoid b) => Maybe a -> m b -> m b Source #
A flip
ed 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 flip
ed 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 flip
ed 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 flip
ed 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 flip
ed version of maybeToEither
.
>>>
maybeToEitherOr (Just "hello") 3
Right "hello"
>>>
maybeToEitherOr Nothing 3
Left 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 Int
3
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 Int
ExceptT (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 Int
ExceptT (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 Bool
ExceptT (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 Bool
ExceptT (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 Bool
ExceptT (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 100
ERROR: couldn't get user name for user 100>>>
nestedPrintFirstCharOfUserName 1
ERROR: user name is empty>>>
nestedPrintFirstCharOfUserName 0
Got 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 100
ERROR: 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 1
ERROR: user name is empty
This time the function succeeds:
>>>
printFirstCharOfUserName 0
Got 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))