packages feed

monad-effect-0.1.0.0: src/Data/Result.hs

-- | Module      : Data.Result
-- Description : A module that provides a Result type for handling errors in a non-empty list of types.
--
-- This module defines a non-empty sum type `EList` parametrized by a list of types,
-- and a `Result es a` type that can either represent a successful computation or a failure in one of the types in the list.
-- 
-- When es is [], it is equivalent to a, there is no redundant constructors or pattern matching.
module Data.Result where

import Data.Kind (Type)
import Data.TypeList.Families

-- | Sum of types, but non-empty by construction.
-- You cannot construct EList '[]
data EList (ts :: [Type]) where
  EHead :: !t -> EList (t : ts)
  ETail :: !(EList ts) -> EList (t : ts)

instance {-# OVERLAPPING #-} Show t => Show (EList '[t]) where
  show (EHead x) = "EHead (" ++ show x ++ ")"
instance (Show t, Show (EList ts)) => Show (EList (t : ts)) where
  show (EHead x)  = "EHead (" ++ show x ++ ")"
  show (ETail xs) = "ETail (" ++ show xs ++ ")"

embedES :: SFirstIndex e ts -> e -> EList ts
embedES SFirstIndexZero e = EHead e
embedES (SFirstIndexSucc _ n) e = ETail (embedES n e)
{-# INLINE embedES #-}

-- | Error type that adapt to the type-level list of errors.
data Result (es :: [Type]) (a :: Type) where
  RSuccess :: a -> Result es a
  RFailure :: !(EList es) -> Result es a

instance {-# OVERLAPPING #-} Show a => Show (Result '[] a) where
  show (RSuccess a) = "RSuccess (" ++ show a ++ ")"
instance (Show (EList es), Show a) => Show (Result es a) where
  show (RSuccess a)  = "RSuccess (" ++ show a ++ ")"
  show (RFailure es) = "RFailure (" ++ show es ++ ")"

instance Functor (Result es) where
  fmap f (RSuccess a)  = RSuccess (f a)
  fmap _ (RFailure es) = RFailure es
  {-# INLINE fmap #-}

instance Applicative (Result es) where
  pure = RSuccess
  RSuccess f <*> RSuccess a = RSuccess (f a)
  RFailure es <*> _ = RFailure es
  _ <*> RFailure es = RFailure es
  {-# INLINE pure #-}
  {-# INLINE (<*>) #-}

instance Monad (Result es) where
  RSuccess a >>= f = f a
  RFailure es >>= _ = RFailure es
  {-# INLINE (>>=) #-}
  return = pure
  {-# INLINE return #-}

getEMaybeS :: SFirstIndex e es -> EList es -> Maybe e
getEMaybeS SFirstIndexZero = \case
  EHead x  -> Just x
  ETail _  -> Nothing
getEMaybeS (SFirstIndexSucc _ n) = \case
  EHead _  -> Nothing
  ETail es -> getEMaybeS n es
{-# INLINE getEMaybeS #-}

resultNoError :: Result '[] a -> a
resultNoError (RSuccess a) = a
{-# INLINE resultNoError #-}

resultToEither :: Result (e ': es) a -> Either (EList (e ': es)) a
resultToEither (RSuccess a)  = Right a
resultToEither (RFailure es) = Left es
{-# INLINE resultToEither #-}

resultMapErrors :: (EList es -> EList es') -> Result es a -> Result es' a
resultMapErrors _ (RSuccess a)  = RSuccess a
resultMapErrors f (RFailure es) = RFailure (f es)
{-# INLINE resultMapErrors #-}

fromElistSingleton :: EList '[s] -> s
fromElistSingleton (EHead x) = x
{-# INLINE fromElistSingleton #-}

getElemRemoveResult :: SNat n -> Result es a -> Either (AtIndex es n) (Result (Remove n es) a)
getElemRemoveResult _         (RSuccess a)          = Right (RSuccess a)
getElemRemoveResult SZero     (RFailure (EHead e))  = Left e
getElemRemoveResult SZero     (RFailure (ETail es)) = Right (RFailure es)
getElemRemoveResult (SSucc _) (RFailure (EHead e))  = Right $ RFailure $ EHead e
getElemRemoveResult (SSucc n) (RFailure (ETail es)) =
  case getElemRemoveResult n (RFailure es) of
    Left  e              -> Left e
    Right (RFailure es') -> Right (RFailure $ ETail es')
    Right (RSuccess a)   -> Right (RSuccess a)
{-# INLINE getElemRemoveResult #-}