inferno-core-0.1.0.0: src/Inferno/Infer/Env.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Inferno.Infer.Env
( Env (..),
Namespace (..),
TypeMetadata (..),
closeOver,
closeOverType,
empty,
lookup,
lookupPinned,
remove,
extend,
merge,
mergeEnvs,
singleton,
keys,
fromList,
fromListModule,
toList,
normtype,
normTC,
fv,
namespaceToIdent,
generalize,
)
where
import Data.Foldable (Foldable (foldl'))
import Data.List (nub)
import qualified Data.Map as Map
import qualified Data.Set as Set
import Inferno.Types.Syntax (ExtIdent)
import Inferno.Types.Type
( ImplType (..),
InfernoType (..),
Namespace (..),
Substitutable (..),
TCScheme (..),
TV (..),
TypeClass (..),
TypeMetadata (..),
namespaceToIdent,
)
import Inferno.Types.VersionControl (VCObjectHash)
import Prelude hiding (lookup)
-------------------------------------------------------------------------------
-- Typing Environment
-------------------------------------------------------------------------------
data Env = TypeEnv
{ types :: Map.Map ExtIdent (TypeMetadata TCScheme),
pinnedTypes :: Map.Map VCObjectHash (TypeMetadata TCScheme)
}
deriving (Eq, Show)
instance Substitutable Env where
apply s env =
env
{ types = Map.map (\meta -> meta {ty = apply s $ ty meta}) $ types env,
pinnedTypes = Map.map (\meta -> meta {ty = apply s $ ty meta}) $ pinnedTypes env
}
ftv env =
ftv $ map ty $ Map.elems $ types env
-- pinnedTypes should not have any free variables!!
empty :: Env
empty = TypeEnv Map.empty Map.empty
extend :: Env -> (ExtIdent, TypeMetadata TCScheme) -> Env
extend env (x, m) = env {types = Map.insert x m (types env)}
remove :: Env -> ExtIdent -> Env
remove env v = env {types = Map.delete v (types env)}
lookup :: ExtIdent -> Env -> Maybe (TypeMetadata TCScheme)
lookup key env = Map.lookup key (types env)
lookupPinned :: VCObjectHash -> Env -> Maybe (TypeMetadata TCScheme)
lookupPinned key env = Map.lookup key (pinnedTypes env)
merge :: Env -> Env -> Env
merge (TypeEnv a b) (TypeEnv a' b') =
TypeEnv (Map.union a a') (Map.union b b')
mergeEnvs :: [Env] -> Env
mergeEnvs = foldl' merge empty
singleton :: ExtIdent -> TypeMetadata TCScheme -> Env
singleton x m =
TypeEnv
{ types = Map.singleton x m,
pinnedTypes = Map.empty
}
keys :: Env -> [ExtIdent]
keys = Map.keys . types
fromList :: [(ExtIdent, TypeMetadata TCScheme)] -> Env
fromList xs =
TypeEnv
{ types = Map.fromList xs,
pinnedTypes = Map.empty
}
fromListModule :: [(VCObjectHash, TypeMetadata TCScheme)] -> Env
fromListModule xs =
TypeEnv
{ types = Map.empty,
pinnedTypes = Map.fromList xs
}
toList :: Env -> [(ExtIdent, TypeMetadata TCScheme)]
toList = Map.toList . types
instance Semigroup Env where
(<>) = merge
instance Monoid Env where
mempty = empty
normTC :: (InfernoType -> InfernoType) -> TypeClass -> TypeClass
normTC nt (TypeClass n tys) = TypeClass n (map nt tys)
fv :: InfernoType -> [TV]
fv (TVar a) = [a]
fv (TArr a b) = fv a ++ fv b
fv (TBase _) = []
fv (TArray t) = fv t
fv (TSeries t) = fv t
fv (TOptional t) = fv t
fv (TTuple ts) = foldr ((++) . fv) [] ts
fv (TRep t) = fv t
normtype :: Map.Map TV TV -> InfernoType -> InfernoType
normtype ord (TArr a b) = TArr (normtype ord a) (normtype ord b)
normtype _ (TBase a) = TBase a
normtype ord (TArray a) = TArray $ normtype ord a
normtype ord (TSeries a) = TSeries $ normtype ord a
normtype ord (TOptional a) = TOptional $ normtype ord a
normtype ord (TTuple as) = TTuple $ fmap (normtype ord) as
normtype ord (TRep a) = TRep $ normtype ord a
normtype ord (TVar a) =
case Map.lookup a ord of
Just x -> TVar x
Nothing -> TVar a -- error $ "type variable " <> show a <> "not in signature"
normalize :: TCScheme -> TCScheme
normalize (ForallTC _ tcs (ImplType impl body)) =
ForallTC
(map snd ord)
(Set.map (normTC $ normtype ordMap) tcs)
$ ImplType (Map.map (normtype ordMap) impl) (normtype ordMap body)
where
-- collect free variables from the body of the function first,
-- then from any implicit type variables and finally from the typeclasses
ftvs = nub $ fv body ++ concatMap (fv . snd) (Map.toList impl) ++ concatMap (\(TypeClass _ tys) -> concatMap fv tys) (Set.toList tcs)
ord = zip ftvs (map TV [0 ..])
ordMap = Map.fromList ord
generalize :: Set.Set TypeClass -> ImplType -> TCScheme
generalize tcs t = ForallTC as tcs t
where
as = Set.toList $ ((ftv t) `Set.union` (Set.unions $ Set.elems $ Set.map ftv tcs))
-- | Canonicalize and return the polymorphic toplevel type.
closeOver :: Set.Set TypeClass -> ImplType -> TCScheme
closeOver tcs t = normalize $ generalize tcs t
closeOverType :: InfernoType -> TCScheme
closeOverType = normalize . generalize Set.empty . ImplType Map.empty