module Blanks.Sub
  ( Sub (..)
  , SubError (..)
  , ThrowSub (..)
  , rethrowSub
  , runSub
  ) where

import Control.Exception (Exception)
import Control.Monad.Except (Except, MonadError (..), runExcept)
import Data.Typeable (Typeable)

data SubError
  = ApplyError !Int !Int
  | UnboundError !Int
  | NonBinderError
  deriving (Eq, Show, Typeable)

instance Exception SubError

class ThrowSub m where
  throwSub :: SubError -> m a

rethrowSub :: (Applicative m, ThrowSub m) => Either SubError a -> m a
rethrowSub = either throwSub pure

newtype Sub a =
  Sub
    { unSub :: Except SubError a
    }
  deriving (Functor, Applicative, Monad, Foldable, Traversable)

instance ThrowSub Sub where
  throwSub = Sub . throwError

runSub :: Sub a -> Either SubError a
runSub = runExcept . unSub