rebound-0.1.0.0: src/Rebound/Env/Functional.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE UndecidableSuperClasses #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use lambda-case" #-}
{-# OPTIONS_HADDOCK hide #-}
module Rebound.Env.Functional where
-- Represents the environment using a function
import Rebound.Lib
import Data.Fin (Fin(..))
import qualified Data.Fin as Fin
import GHC.Generics hiding (S)
------------------------------------------------------------------------------
-- Substitution class declarations
------------------------------------------------------------------------------
-- | Well-scoped types that can be the range of
-- an environment. This should generally be the @Var@
-- constructor from the syntax.
class (Subst v v) => SubstVar (v :: Nat -> Type) where
var :: Fin n -> v n
-- | Apply the environment throughout a term of
-- type `c n`, replacing variables with values
-- of type `v m`
class (SubstVar v) => Subst v c where
applyE :: Env v n m -> c n -> c m
default applyE :: (Generic1 c, GSubst v (Rep1 c), SubstVar v) => Env v m n -> c m -> c n
applyE = gapplyE
{-# INLINE applyE #-}
isVar :: c n -> Maybe (v :~: c, Fin n)
isVar _ = Nothing
{-# INLINE isVar #-}
-- Generic programming
class GSubst v (e :: Nat -> Type) where
gsubst :: Env v m n -> e m -> e n
gapplyE :: forall v c m n. (Generic1 c, GSubst v (Rep1 c), Subst v c) => Env v m n -> c m -> c n
gapplyE r e | Just (Refl, x) <- isVar @v @c e = applyEnv r x
gapplyE r e = applyOpt (\s x -> to1 $ gsubst s (from1 x)) r e
{-# INLINEABLE gapplyE #-}
------------------------------------------------------------------------------
-- Environment representation as finite function
------------------------------------------------------------------------------
newtype Env (a :: Nat -> Type) (n :: Nat) (m :: Nat) =
Env { applyEnv :: Fin n -> a m }
------------------------------------------------------------------------------
-- Application
------------------------------------------------------------------------------
-- | Build an optimized version of applyE (does nothing here)
applyOpt :: (Env v n m -> c n -> c m) -> (Env v n m -> c n -> c m)
applyOpt f = f
{-# INLINEABLE applyOpt #-}
------------------------------------------------------------------------------
-- Construction and modification
------------------------------------------------------------------------------
-- | The empty environment (zero domain)
zeroE :: Env v Z n
zeroE = Env $ \ x -> case x of {}
{-# INLINEABLE zeroE #-}
-- make the bound bigger, on the right, but do not change any indices.
-- this is an identity function
weakenER :: forall m v n. (SubstVar v) => SNat m -> Env v n (n + m)
weakenER m = Env $ \x -> var (Fin.weakenFinRight m x)
{-# INLINEABLE weakenER #-}
-- make the bound bigger, on the left, but do not change any indices.
-- this is an identity function
weakenE' :: forall m v n. (SubstVar v) => SNat m -> Env v n (m + n)
weakenE' m = Env $ \x -> var (Fin.weakenFin m x)
{-# INLINEABLE weakenE' #-}
-- | increment all free variables by m
shiftNE :: (SubstVar v) => SNat m -> Env v n (m + n)
shiftNE m = Env $ \x -> var (Fin.shiftN m x)
{-# INLINEABLE shiftNE #-}
-- | @cons@ -- extend an environment with a new mapping
-- for index '0'. All existing mappings are shifted over.
(.:) :: SubstVar v => v m -> Env v n m -> Env v (S n) m
ty .: s = Env $ \y -> case y of
FZ -> ty
FS x -> applyEnv s x
{-# INLINEABLE (.:) #-}
-- | inverse of @cons@ -- remove the first mapping
tail :: (SubstVar v) => Env v (S n) m -> Env v n m
tail x = shiftNE s1 .>> x
{-# INLINEABLE tail #-}
-- | composition: do f then g
(.>>) :: (Subst v v) => Env v p n -> Env v n m -> Env v p m
(.>>) = comp
{-# INLINEABLE (.>>) #-}
-- | smart constructor for composition
comp :: forall a m n p. SubstVar a =>
Env a m n -> Env a n p -> Env a m p
comp s1 s2 = Env $ \x -> applyE s2 (applyEnv s1 x)
{-# INLINEABLE comp #-}
-- | modify an environment so that it can go under a binder
up :: (SubstVar v) => Env v m n -> Env v (S m) (S n)
up e = var Fin.f0 .: comp e (shiftNE s1)
{-# INLINEABLE up #-}
-- | mapping operation for range of the environment
transform :: (forall m. a m -> b m) -> Env a n m -> Env b n m
transform f g = Env $ \x -> f (applyEnv g x)
{-# INLINEABLE transform #-}