{-| Module : Result Description : A `Result` is the result of a computation that may fail. This is a great way to manage errors. License : BSD 3 Maintainer : terezasokol@gmail.com Stability : experimental Portability : POSIX A `Result` is the result of a computation that may fail. This is a great way to manage errors. -} module Result ( Result(..) -- * Mapping , map, map2, map3, map4, map5 -- * Chaining , andThen -- * Handling Errors , withDefault, toMaybe, fromMaybe, mapError -- * Conversions from Haskell Types , fromEither ) where import Prelude (Applicative, Char, Eq, Functor, Monad, Num, Ord, Show, flip, fromIntegral, mappend, mconcat, otherwise, pure, (<*>), (>>=), fmap) import Maybe (Maybe(Just, Nothing)) import qualified Data.Either import qualified Internal.Shortcut as Shortcut {-| A `Result` is either `Ok` meaning the computation succeeded, or it is an `Err` meaning that there was some failure. -} data Result error success = Ok success | Err error deriving (Prelude.Show, Prelude.Eq) instance Functor (Result error) where fmap func result = case result of Ok success -> Ok (func success) Err error -> Err error instance Applicative (Result error) where pure = Ok (<*>) r1 r2 = case (r1, r2) of (Ok func, Ok a) -> Ok (func a) (Err err, _) -> Err err (Ok _, Err err) -> Err err instance Monad (Result error) where (>>=) result func = case result of Ok success -> func success Err error -> Err error {-| If the result is `Ok` return the value, but if the result is an `Err` then return a given default value. The following examples try to parse integers. > Result.withDefault 0 (Ok 123) == 123 > Result.withDefault 0 (Err "no") == 0 -} withDefault :: a -> Result b a -> a withDefault fallback result = case result of Ok success -> success Err _ -> fallback {-| Apply a function to a result. If the result is `Ok`, it will be converted. If the result is an `Err`, the same error value will propagate through. map sqrt (Ok 4.0) == Ok 2.0 map sqrt (Err "bad input") == Err "bad input" -} map :: (a -> b) -> Result c a -> Result c b map = Shortcut.map {-| Apply a function if both results are `Ok`. If not, the first `Err` will propagate through. > map2 max (Ok 42) (Ok 13) == Ok 42 > map2 max (Err "x") (Ok 13) == Err "x" > map2 max (Ok 42) (Err "y") == Err "y" > map2 max (Err "x") (Err "y") == Err "x" This can be useful if you have two computations that may fail, and you want to put them together quickly. -} map2 :: (a -> b -> c) -> Result err a -> Result err b -> Result err c map2 = Shortcut.map2 {-|-} map3 :: (a -> b -> c -> d) -> Result err a -> Result err b -> Result err c -> Result err d map3 = Shortcut.map3 {-|-} map4 :: (a -> b -> c -> d -> e) -> Result err a -> Result err b -> Result err c -> Result err d -> Result err e map4 = Shortcut.map4 {-|-} map5 :: (a -> b -> c -> d -> e -> f) -> Result err a -> Result err b -> Result err c -> Result err d -> Result err e -> Result err f map5 = Shortcut.map5 {-| Chain together a sequence of computations that may fail. It is helpful to see its definition: > andThen :: (a -> Result e b) -> Result e a -> Result e b > andThen callback result = > case result of > Ok value -> callback value > Err msg -> Err msg This means we only continue with the callback if things are going well. For example, say you need to use (`toInt :: String -> Result String Int`) to parse a month and make sure it is between 1 and 12: > toValidMonth :: Int -> Result String Int > toValidMonth month = > if month >= 1 && month <= 12 > then Ok month > else Err "months must be between 1 and 12" > toMonth :: String -> Result String Int > toMonth rawString = > toInt rawString > |> andThen toValidMonth > -- toMonth "4" == Ok 4 > -- toMonth "9" == Ok 9 > -- toMonth "a" == Err "cannot parse to an Int" > -- toMonth "0" == Err "months must be between 1 and 12" This allows us to come out of a chain of operations with quite a specific error message. It is often best to create a custom type that explicitly represents the exact ways your computation may fail. This way it is easy to handle in your code. -} andThen :: (a -> Result c b) -> Result c a -> Result c b andThen = Shortcut.andThen {-| Transform an `Err` value. For example, say the errors we get have too much information: > parseInt :: String -> Result ParseError Int > > data ParseError = ParseError > { message :: String > , code :: Int > , position :: (Int,Int) > } > > mapError message (parseInt "123") == Ok 123 > mapError message (parseInt "abc") == Err "char 'a' is not a number" -} mapError :: (a -> b) -> Result a c -> Result b c mapError func result = case result of Ok success -> Ok success Err error -> Err (func error) {-| Convert to a simpler `Maybe` if the actual error message is not needed or you need to interact with some code that primarily uses maybes. > parseInt :: String -> Result ParseError Int > > maybeParseInt :: String -> Maybe Int > maybeParseInt string = > toMaybe (parseInt string) -} toMaybe :: Result a b -> Maybe b toMaybe result = case result of Ok success -> Just success Err _ -> Nothing {-| Convert from a simple `Maybe` to interact with some code that primarily uses `Results`. > parseInt :: String -> Maybe Int > > resultParseInt :: String -> Result String Int > resultParseInt string = > fromMaybe ("error parsing string: " ++ toString string) (parseInt string) -} fromMaybe :: a -> Maybe b -> Result a b fromMaybe error maybe = case maybe of Just something -> Ok something Nothing -> Err error {-| -} fromEither :: Data.Either.Either x a -> Result x a fromEither either = case either of Data.Either.Right a -> Ok a Data.Either.Left x -> Err x