module Data.Result where

import Prologue

import Control.Monad.Poly
import Control.Applicative.Poly
import Data.Maybe               (fromJust)

-----------------------------
-- === Result wrappers === --
-----------------------------

data Ok      a = Ok a  deriving (Show, Functor, Foldable, Traversable)
data Error e a = Error deriving (Show)


-- Utils

fromOk :: Ok a -> a
fromOk (Ok a) = a

class    MaybeResult m         where maybeResult :: m a -> Maybe a
instance MaybeResult Ok        where maybeResult = Just  unwrap'
instance MaybeResult (Error e) where maybeResult = const Nothing

unsafeFromResult :: MaybeResult m => m a -> a
unsafeFromResult = fromJust  maybeResult


class    IfOk m         where ifOk :: m a -> (a -> b) -> b -> b
instance IfOk Ok        where ifOk (Ok a) f _ = f a 
instance IfOk (Error e) where ifOk _ _ e      = e 

-- Basic instances 

instance Applicative Ok where pure = Ok
                              Ok f <*> Ok a = Ok $ f a

instance Monad       Ok where return = Ok
                              Ok a >>= f = f a


instance Functor     (Error e) where fmap f e = Error
instance Applicative (Error e) where pure _   = Error
                                     _ <*> _  = Error

instance Monad       (Error e) where return _ = Error
                                     _ >>= f  = Error


-- Wrappers
instance      Coated    Ok
type instance Unlayered (Ok a) = a
instance      Layered   (Ok a)
instance      Rewrapped (Ok a) (Ok a')
instance      Wrapped   (Ok a) where
	type      Unwrapped (Ok a) = a
	_Wrapped' = iso (\(Ok a) -> a) Ok

-- Poly instances

type instance PolyBind Ok        Ok         = Ok
type instance PolyBind (Error e) a          = Error e
type instance PolyBind Ok        (Error e)  = Error e

instance {-# OVERLAPPABLE #-} PolyApplicative (Error e) a        where _ <<*>> _ = Error
instance {-# OVERLAPPABLE #-} PolyApplicative (Error e)(Error e) where _ <<*>> _ = Error
instance {-# OVERLAPPABLE #-} PolyApplicative Ok       (Error e) where _ <<*>> _ = Error

instance PolyMonad (Error e) a         where _    >>>= _ = Error
instance PolyMonad Ok        (Error e) where Ok a >>>= f = f a


-------------------------------
-- === Result constrains === --
-------------------------------

class CompilationError a

class                          Check a (ok :: Bool)
instance                       Check a 'True
instance CompilationError a => Check a 'False

type family Asserted e check where Asserted e 'True  = Ok
                                   Asserted e 'False = Error e

class    Assert e (ok :: Bool) where assert :: Proxy e -> Proxy ok -> a -> Asserted e ok a
instance Assert e 'True        where assert _ _   = Ok
instance Assert e 'False       where assert _ _ _ = Error