packages feed

atp-0.1.0.0: src/ATP/FirstOrder/Alpha.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

{-|
Module       : ATP.FirstOrder.Alpha
Description  : Monads and monad transformers for computations with free and
               bound variables.
Copyright    : (c) Evgenii Kotelnikov, 2019-2021
License      : GPL-3
Maintainer   : evgeny.kotelnikov@gmail.com
Stability    : experimental
-}

module ATP.FirstOrder.Alpha (
  AlphaT,
  evalAlphaT,
  Alpha,
  evalAlpha,
  lookup,
  scope,
  enter,
  share,
  MonadAlpha(..)
) where

import Prelude hiding (lookup)
import Control.Applicative ((<|>))
import Control.Monad.Trans (MonadTrans(..))
import Control.Monad.Reader (MonadReader(..), ReaderT(..), asks)
import Control.Monad.State (MonadState(..), StateT(..), modify, gets)
import Data.Functor.Identity (Identity(..))
import qualified Data.List as L (lookup)
import qualified Data.Map as M (empty, lookup, insert, elems)
import Data.Map (Map)

import ATP.FirstOrder.Core


-- | The stack of renamings for the bound variables in the expression.
type Stack = [(Var, Var)]

-- | The rename mapping for the free variables in the expression.
type Global = Map Var Var

-- | The monad transformer that adds to the given monad @m@ the ability to track
-- a renaming of free and bound variables in a first-order expression.
newtype AlphaT m a = AlphaT (ReaderT Stack (StateT Global m) a)
  deriving (Functor, Applicative, Monad, MonadReader Stack, MonadState Global)

instance MonadTrans AlphaT where
  lift = AlphaT . lift . lift

runAlphaT :: AlphaT m a -> m (a, Global)
runAlphaT (AlphaT r) = runStateT (runReaderT r []) M.empty

-- | Evaluate an alpha computation and return the final value,
-- discarding the global scope.
evalAlphaT :: Monad m => AlphaT m a -> m a
evalAlphaT = fmap fst . runAlphaT


-- | The alpha monad parametrized by the type @a@ of the return value.
type Alpha a = AlphaT Identity a

-- | Evaluate an 'Alpha' computation and return the final value,
-- discarding the final variable renaming.
evalAlpha :: Alpha a -> a
evalAlpha = runIdentity . evalAlphaT


-- | Lookup a variable, first in the stack of bound variables,
-- then in the global scope.
lookup :: Monad m => Var -> AlphaT m (Maybe Var)
lookup v = do
  bv <- asks (L.lookup v)
  fv <- gets (M.lookup v)
  return (bv <|> fv)

-- | Read the set of free and bound variables of the given 'AlphaT' computation.
scope :: Monad m => AlphaT m [Var]
scope = do
  bv <- asks (fmap snd)
  fv <- gets M.elems
  return (bv ++ fv)

-- | Run a computation inside 'AlphaT' with the variable renaming.
enter :: Monad m => Var -> Var -> AlphaT m a -> AlphaT m a
enter v w = local ((v,w):)

-- | Update the global scope with a variable renaming.
share :: Monad m => Var -> Var -> AlphaT m ()
share v w = modify (M.insert v w)


-- | A helper monad for computations on free and bound occurrences of variables.
class Monad m => MonadAlpha m where
  -- | A monadic action to perform on a variable under a quantifier.
  binding :: Var -> AlphaT m Var

  -- | A monadic action to perform on a variable occurrence.
  occurrence :: Var -> AlphaT m Var