{-# LANGUAGE MultiParamTypeClasses, UndecidableInstances #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.Extensible.Effect.Default (
  ReaderDef
  , runReaderDef
  , StateDef
  , runStateDef
  , evalStateDef
  , execStateDef
  , WriterDef
  , runWriterDef
  , execWriterDef
  , MaybeDef
  , runMaybeDef
  , EitherDef
  , runEitherDef
) where
import Control.Applicative
import Data.Extensible.Effect
import Data.Extensible.Internal
import Control.Monad.Except
import Control.Monad.Catch
import Control.Monad.Reader.Class
import Control.Monad.Skeleton
import Control.Monad.State.Strict
#if MIN_VERSION_resourcet(1,2,0)
import Control.Monad.Trans.Resource
#endif
import Control.Monad.Writer.Class
instance (MonadIO m, Associate "IO" m xs) => MonadIO (Eff xs) where
  liftIO = liftEff (Proxy :: Proxy "IO") . liftIO
#if MIN_VERSION_resourcet(1,2,0)
instance (MonadResource m, Associate "IO" m xs) => MonadResource (Eff xs) where
  liftResourceT = liftEff (Proxy :: Proxy "IO") . liftResourceT
#endif
instance (MonadThrow m, Associate "IO" m xs) => MonadThrow (Eff xs) where
  throwM = liftEff (Proxy :: Proxy "IO") . throwM
instance (MonadCatch m, Associate "IO" m xs) => MonadCatch (Eff xs) where
  catch m0 h = go m0 where
    go m = case debone m of
      Return a -> return a
      Instruction i t :>>= k -> case compareMembership (association :: Membership xs ("IO" ':> m)) i of
        Left _ -> boned $ Instruction i t :>>= go . k
        Right Refl -> boned $ Instruction i (try t) :>>= go . either h k
pReader :: Proxy "Reader"
pReader = Proxy
instance Associate "Reader" ((:~:) r) xs => MonadReader r (Eff xs) where
  ask = askEff pReader
  local = localEff pReader
  reader = asksEff pReader
pState :: Proxy "State"
pState = Proxy
instance Associate "State" (State s) xs => MonadState s (Eff xs) where
  get = getEff pState
  put = putEff pState
  state = stateEff pState
pWriter :: Proxy "Writer"
pWriter = Proxy
instance (Monoid w, Associate "Writer" ((,) w) xs) => MonadWriter w (Eff xs) where
  writer = writerEff pWriter
  tell = tellEff pWriter
  listen = listenEff pWriter
  pass = passEff pWriter
pEither :: Proxy "Either"
pEither = Proxy
instance (Associate "Either" (Const e) xs) => MonadError e (Eff xs) where
  throwError = throwEff pEither
  catchError = catchEff pEither
instance (Monoid e, Associate "Either" (Const e) xs) => Alternative (Eff xs) where
  empty = throwError mempty
  p <|> q = catchError p (const q)
instance (Monoid e, Associate "Either" (Const e) xs) => MonadPlus (Eff xs) where
  mzero = empty
  mplus = (<|>)
type ReaderDef r = "Reader" >: ReaderEff r
runReaderDef :: Eff (ReaderDef r ': xs) a -> r -> Eff xs a
runReaderDef = runReaderEff
{-# INLINE runReaderDef #-}
type StateDef s = "State" >: State s
runStateDef :: Eff (StateDef s ': xs) a -> s -> Eff xs (a, s)
runStateDef = runStateEff
{-# INLINE runStateDef #-}
evalStateDef :: Eff (StateDef s ': xs) a -> s -> Eff xs a
evalStateDef = evalStateEff
{-# INLINE evalStateDef #-}
execStateDef :: Eff (StateDef s ': xs) a -> s -> Eff xs s
execStateDef = execStateEff
{-# INLINE execStateDef #-}
type WriterDef w = "Writer" >: WriterEff w
runWriterDef :: Monoid w => Eff (WriterDef w ': xs) a -> Eff xs (a, w)
runWriterDef = runWriterEff
{-# INLINE runWriterDef #-}
execWriterDef :: Monoid w => Eff (WriterDef w ': xs) a -> Eff xs w
execWriterDef = execWriterEff
{-# INLINE execWriterDef #-}
type MaybeDef = "Either" >: EitherEff ()
runMaybeDef :: Eff (MaybeDef ': xs) a -> Eff xs (Maybe a)
runMaybeDef = runMaybeEff
{-# INLINE runMaybeDef #-}
type EitherDef e = "Either" >: EitherEff e
runEitherDef :: Eff (EitherDef e ': xs) a -> Eff xs (Either e a)
runEitherDef = runEitherEff
{-# INLINE runEitherDef #-}