module Control.Monad.Trans.Fresh (FreshT, evalFreshT, Fresh, evalFresh, withName, Name) where

import "base" Prelude (Show (..), (+), flip, ($), Eq (..))
import qualified "base" Data.Functor as Hs

import Control.Applicative (Applicative (..), Alternative (..))
import Control.Monad (Monad (..), MonadPlus (..))
import Control.Monad.Trans.Reader
import qualified Data.List as List (genericIndex, reverse)

newtype FreshT m a = FreshT { unFreshT :: ReaderT Natural m a }
  deriving newtype (Hs.Functor, Applicative, Monad, Alternative, MonadPlus)

newtype Name i = Name Natural
  deriving (Eq)

instance Category s => Functor s (->) Name where
    map _ (Name n) = Name n

instance Show (Name i) where
    show (Name n) = List.genericIndex names n
      where names = map List.reverse $ flip (:) <$> "":names <*> (['a'..'z'] <|> ['α'..'ω'])

evalFreshT :: FreshT m a -> m a
evalFreshT = flip runReaderT 0 . unFreshT

type Fresh = FreshT Identity

evalFresh :: Fresh a -> a
evalFresh = runIdentity . evalFreshT

withName :: Monad m => (Name i -> FreshT m a) -> FreshT m a
withName f = FreshT (asks Name) >>= FreshT . local (+1) . unFreshT . f