ddc-core-0.4.3.1: DDC/Core/Env/EnvT.hs
-- | Environment of a type expression.
--
-- An environment contains the types
-- named bound variables,
-- named primitives,
-- and a deBruijn stack for anonymous variables.
--
module DDC.Core.Env.EnvT
( EnvT(..)
-- * Construction
, empty
, singleton
, extend, extends
, union, unions
-- * Conversion
, fromList
, fromListNT
, fromTypeMap
, kindEnvOfEnvT
-- * Projections
, depth
, member, memberBind
, lookup, lookupName
-- * Primitives
, setPrimFun
, isPrim
-- * Lifting
, lift)
where
import DDC.Type.Exp
import DDC.Type.Transform.BoundT
import Data.Maybe
import Data.Map (Map)
import Prelude hiding (lookup)
import qualified DDC.Type.Env as Env
import qualified Data.Map.Strict as Map
import qualified Prelude as P
import Control.Monad
-- | A type environment.
data EnvT n
= EnvT
{ -- | Types of baked in, primitive names.
envtPrimFun :: !(n -> Maybe (Type n))
-- | Map of constructor name to bound type for type equations.
, envtEquations :: !(Map n (Type n))
-- | Map of globally available capabilities.
, envtCapabilities :: !(Map n (Type n))
-- | Kinds of named variables and constructors.
, envtMap :: !(Map n (Type n))
-- | Types of anonymous deBruijn variables.
, envtStack :: ![Type n]
-- | The length of the above stack.
, envtStackLength :: !Int }
-- | An empty environment.
empty :: EnvT n
empty = EnvT
{ envtPrimFun = \_ -> Nothing
, envtEquations = Map.empty
, envtCapabilities = Map.empty
, envtMap = Map.empty
, envtStack = []
, envtStackLength = 0 }
-- | Construct a singleton type environment.
singleton :: Ord n => Bind n -> EnvT n
singleton b
= extend b empty
-- | Extend an environment with a new binding.
-- Replaces bindings with the same name already in the environment.
extend :: Ord n => Bind n -> EnvT n -> EnvT n
extend bb env
= case bb of
BName n k -> env { envtMap = Map.insert n k (envtMap env) }
BAnon k -> env { envtStack = k : envtStack env
, envtStackLength = envtStackLength env + 1 }
BNone{} -> env
-- | Extend an environment with a list of new bindings.
-- Replaces bindings with the same name already in the environment.
extends :: Ord n => [Bind n] -> EnvT n -> EnvT n
extends bs env
= foldl (flip extend) env bs
-- | Set the function that knows the types of primitive things.
setPrimFun :: (n -> Maybe (Type n)) -> EnvT n -> EnvT n
setPrimFun f env
= env { envtPrimFun = f }
-- | Check if the type of a name is defined by the `envPrimFun`.
isPrim :: EnvT n -> n -> Bool
isPrim env n
= isJust $ envtPrimFun env n
-- | Convert a list of `Bind`s to an environment.
fromList :: Ord n => [Bind n] -> EnvT n
fromList bs
= foldr extend empty bs
-- | Convert a list of name and types into an environment
fromListNT :: Ord n => [(n, Type n)] -> EnvT n
fromListNT nts
= fromList [BName n t | (n, t) <- nts]
-- | Convert a map of names to types to a environment.
fromTypeMap :: Map n (Type n) -> EnvT n
fromTypeMap m
= empty { envtMap = m}
-- | Extract a `KindEnv` from an `EnvT`.
kindEnvOfEnvT :: Ord n => EnvT n -> Env.KindEnv n
kindEnvOfEnvT env
= Env.empty
{ Env.envMap = envtMap env
, Env.envPrimFun = \n -> envtPrimFun env n }
-- | Combine two environments.
-- If both environments have a binding with the same name,
-- then the one in the second environment takes preference.
union :: Ord n => EnvT n -> EnvT n -> EnvT n
union env1 env2
= EnvT
{ envtMap = envtMap env1 `Map.union` envtMap env2
, envtStack = envtStack env2 ++ envtStack env1
, envtStackLength = envtStackLength env2 + envtStackLength env1
, envtEquations = envtEquations env1 `Map.union` envtEquations env2
, envtCapabilities = envtCapabilities env1 `Map.union` envtCapabilities env2
, envtPrimFun = \n -> envtPrimFun env2 n `mplus` envtPrimFun env1 n }
-- | Combine multiple environments,
-- with the latter ones taking preference.
unions :: Ord n => [EnvT n] -> EnvT n
unions envs
= foldr union empty envs
-- | Check whether a bound variable is present in an environment.
member :: Ord n => Bound n -> EnvT n -> Bool
member uu env
= isJust $ lookup uu env
-- | Check whether a binder is already present in the an environment.
-- This can only return True for named binders, not anonymous or primitive ones.
memberBind :: Ord n => Bind n -> EnvT n -> Bool
memberBind uu env
= case uu of
BName n _ -> Map.member n (envtMap env)
_ -> False
-- | Lookup a bound variable from an environment.
lookup :: Ord n => Bound n -> EnvT n -> Maybe (Type n)
lookup uu env
= case uu of
UName n
-> Map.lookup n (envtMap env)
`mplus` envtPrimFun env n
UIx i -> P.lookup i (zip [0..] (envtStack env))
UPrim n _ -> envtPrimFun env n
-- | Lookup a bound name from an environment.
lookupName :: Ord n => n -> EnvT n -> Maybe (Type n)
lookupName n env
= Map.lookup n (envtMap env)
-- | Yield the total depth of the deBruijn stack.
depth :: EnvT n -> Int
depth env = envtStackLength env
-- | Lift all free deBruijn indices in the environment by the given number of steps.
---
-- ISSUE #276: Delay lifting of indices in type environments.
-- The 'lift' function on type environments applies to every member of
-- the environment. We'd get better complexity by recording how many
-- levels all types should be lifted by, and only applying the real lift
-- function when the type is finally extracted.
--
lift :: Ord n => Int -> EnvT n -> EnvT n
lift n env
= EnvT
{ envtMap = Map.map (liftT n) (envtMap env)
, envtStack = map (liftT n) (envtStack env)
, envtStackLength = envtStackLength env
, envtEquations = envtEquations env
, envtCapabilities = envtCapabilities env
, envtPrimFun = envtPrimFun env }