egison-5.0.0: hs-src/Language/Egison/EnvBuilder.hs
{- |
Module : Language.Egison.EnvBuilder
Licence : MIT
This module implements Phase 2: Environment Building Phase.
It collects all declarations from TopExpr list before type inference and evaluation.
Environment Building Phase (Phase 2):
1. Data constructor definitions collection (from InductiveDecl)
2. Type class definitions collection (from ClassDeclExpr)
3. Instance definitions collection (from InstanceDeclExpr)
4. Type signature collection (from Define, DefineWithType)
This phase must be completed BEFORE type inference (Phase 5) begins,
ensuring all necessary information is available for type checking.
-}
module Language.Egison.EnvBuilder
( buildEnvironments
, EnvBuildResult(..)
) where
import Control.Monad (foldM)
import Control.Monad.Except (throwError)
import Control.Monad.State
import Data.Char (toUpper, toLower)
import qualified Data.HashMap.Strict as HashMap
import Language.Egison.AST
import Language.Egison.Data (EvalM)
import Language.Egison.EvalState (ConstructorInfo(..), ConstructorEnv, PatternConstructorEnv)
import Language.Egison.IExpr (Var(..), Index(..), stringToVar)
import Language.Egison.Desugar (transVarIndex)
import Language.Egison.Type.Env (TypeEnv, ClassEnv, PatternTypeEnv, emptyEnv, emptyClassEnv, emptyPatternEnv,
extendEnv, extendPatternEnv, addClass, addInstance, lookupClass)
import qualified Language.Egison.Type.Types as Types
import Language.Egison.Type.Types (Type(..), TyVar(..), Constraint(..), TypeScheme(..), TensorShape(..),
ClassInfo, InstanceInfo, freeTyVars, typeToName, sanitizeMethodName, typeExprToType,
capitalizeFirst, lowerFirst)
import qualified Data.Set as Set
-- | Result of environment building phase
data EnvBuildResult = EnvBuildResult
{ ebrTypeEnv :: TypeEnv -- ^ Type signatures for definitions
, ebrClassEnv :: ClassEnv -- ^ Type class and instance information
, ebrConstructorEnv :: ConstructorEnv -- ^ Data constructor information
, ebrPatternConstructorEnv :: PatternConstructorEnv -- ^ Pattern constructor information
, ebrPatternTypeEnv :: PatternTypeEnv -- ^ Pattern function information
} deriving (Show)
--------------------------------------------------------------------------------
-- Phase 2: Environment Building Phase
--------------------------------------------------------------------------------
-- | Build all environments from a list of top-level expressions.
-- This function implements Phase 2 of the processing flow.
-- It must be called AFTER expandLoads (Phase 1) and BEFORE type inference (Phase 5).
buildEnvironments :: [TopExpr] -> EvalM EnvBuildResult
buildEnvironments exprs = do
-- Start with empty environments
let initialResult = EnvBuildResult
{ ebrTypeEnv = emptyEnv
, ebrClassEnv = emptyClassEnv
, ebrConstructorEnv = HashMap.empty
, ebrPatternConstructorEnv = emptyPatternEnv
, ebrPatternTypeEnv = emptyPatternEnv
}
-- Process each top-level expression to collect declarations
foldM processTopExpr initialResult exprs
-- | Process a single top-level expression to collect environment information
processTopExpr :: EnvBuildResult -> TopExpr -> EvalM EnvBuildResult
processTopExpr result topExpr = case topExpr of
-- 1. Data Constructor Definitions (from InductiveDecl)
InductiveDecl typeName typeParams constructors -> do
let typeParamVars = map (TVar . TyVar) typeParams
adtType = TInductive typeName typeParamVars
typeEnv = ebrTypeEnv result
ctorEnv = ebrConstructorEnv result
-- Register each constructor
(typeEnv', ctorEnv') <- foldM (registerConstructor typeName typeParams adtType)
(typeEnv, ctorEnv)
constructors
return result { ebrTypeEnv = typeEnv', ebrConstructorEnv = ctorEnv' }
-- 2. Type Class Definitions (from ClassDeclExpr)
ClassDeclExpr (ClassDecl className [typeParam] superClasses methods) -> do
let classEnv = ebrClassEnv result
typeEnv = ebrTypeEnv result
tyVar = TyVar typeParam
-- Extract superclass names from ConstraintExprs
superNames = map extractConstraintName superClasses
-- Build method list with types
methodsWithTypes = map extractMethodWithType methods
-- Create ClassInfo
-- Note: Use qualified name to avoid ambiguity with ClassDecl.classMethods
classInfo = Types.ClassInfo
{ Types.classSupers = superNames
, Types.classParam = tyVar
, Types.classMethods = methodsWithTypes
}
-- Register class
classEnv' = addClass className classInfo classEnv
-- Register each class method to type environment
typeEnv' = foldl (registerClassMethod tyVar className) typeEnv methods
return result { ebrClassEnv = classEnv', ebrTypeEnv = typeEnv' }
ClassDeclExpr _ ->
-- Unsupported class declaration format (multiple type parameters, etc.)
return result
-- 3. Instance Definitions (from InstanceDeclExpr)
InstanceDeclExpr (InstanceDecl context className instTypes methods) -> do
let classEnv = ebrClassEnv result
typeEnv = ebrTypeEnv result
-- Get the main instance type
mainInstType = case instTypes of
[] -> TAny
(t:_) -> typeExprToType t
-- Create InstanceInfo
instInfo = Types.InstanceInfo
{ Types.instContext = map constraintToInternal context
, Types.instClass = className
, Types.instType = mainInstType
, Types.instMethods = [] -- Methods are handled during desugaring/evaluation
}
-- Register instance
classEnv' = addInstance className instInfo classEnv
-- Register method type signatures for generated methods
-- This prevents "Unbound variable" warnings during type inference
-- Pass the instance context (constraints) to include in method types
typeEnv' = registerInstanceMethods className mainInstType (map constraintToInternal context) methods classEnv' typeEnv
return result { ebrClassEnv = classEnv', ebrTypeEnv = typeEnv' }
-- 4. Type Signature Collection (from Define, DefineWithType)
-- Note: We only collect explicit type signatures here.
-- Inferred types will be added during type inference.
DefineWithType typedVar _expr -> do
let name = typedVarName typedVar
varIndices = typedVarIndices typedVar
-- Convert VarIndex to Index (Maybe Var) - like transVarIndex but with Nothing content
indexTypes = concatMap transVarIndex varIndices
-- Create Var with index structure (content is Just Var, so map to Nothing)
var = Var name (map (fmap (const Nothing)) indexTypes)
params = typedVarParams typedVar
retType = typeExprToType (typedVarRetType typedVar)
paramTypes = map typedParamToType params
-- Build function type
funType = foldr TFun retType paramTypes
-- Convert constraints from AST to internal representation
constraints = map constraintToInternal (typedVarConstraints typedVar)
-- Generalize free type variables in the type signature
-- This handles type parameters like {a, b, c} in def compose {a, b, c} ...
freeVars = Set.toList (freeTyVars funType)
typeScheme = Types.Forall freeVars constraints funType
typeEnv = ebrTypeEnv result
typeEnv' = extendEnv var typeScheme typeEnv
return result { ebrTypeEnv = typeEnv' }
-- 5. Pattern Inductive Declarations (from PatternInductiveDecl)
PatternInductiveDecl typeName typeParams constructors -> do
let typeParamVars = map (TVar . TyVar) typeParams
-- Special cases: [a] as TCollection and String as TString
patternType = case (typeName, typeParams) of
("[]", [param]) -> TCollection (TVar (TyVar param))
("String", []) -> TString
_ -> TInductive typeName typeParamVars
patternCtorEnv = ebrPatternConstructorEnv result
-- Register each pattern constructor to pattern constructor environment
patternCtorEnv' <- foldM (registerPatternConstructor typeName typeParams patternType)
patternCtorEnv
constructors
return result { ebrPatternConstructorEnv = patternCtorEnv' }
-- 6. Pattern Function Declarations (from PatternFunctionDecl)
PatternFunctionDecl name typeParams params retType _body -> do
let paramTypes = map (typeExprToType . snd) params
retType' = typeExprToType retType
-- Pattern function type: arg1 -> arg2 -> ... -> retType (without Pattern wrapper)
patternFuncType = foldr TFun retType' paramTypes
-- Quantify over type parameters
tyVars = map TyVar typeParams
typeScheme = Types.Forall tyVars [] patternFuncType
patternEnv = ebrPatternTypeEnv result
patternEnv' = extendPatternEnv name typeScheme patternEnv
return result { ebrPatternTypeEnv = patternEnv' }
-- Other expressions don't contribute to environment building
Define {} -> return result
DefineWithType {} -> return result
Test {} -> return result
Execute {} -> return result
LoadFile {} -> return result -- Should not appear after expandLoads
InfixDecl {} -> return result
-- 7. Symbol Declarations (from DeclareSymbol)
DeclareSymbol names mTypeExpr -> do
let ty = case mTypeExpr of
Just texpr -> typeExprToType texpr
Nothing -> TInt -- Default to Integer (MathExpr)
scheme = Forall [] [] ty
typeEnv = ebrTypeEnv result
-- Add each symbol to the type environment
typeEnv' = foldr (\name env -> extendEnv (stringToVar name) scheme env) typeEnv names
return result { ebrTypeEnv = typeEnv' }
--------------------------------------------------------------------------------
-- Helper Functions
--------------------------------------------------------------------------------
-- | Register a single data constructor
registerConstructor :: String -> [String] -> Type
-> (TypeEnv, ConstructorEnv) -> InductiveConstructor
-> EvalM (TypeEnv, ConstructorEnv)
registerConstructor typeName typeParams resultType (typeEnv, ctorEnv)
(InductiveConstructor ctorName argTypeExprs) = do
let argTypes = map typeExprToType argTypeExprs
-- Constructor type: argTypes -> resultType
constructorType = foldr TFun resultType argTypes
-- Quantify over type parameters
tyVars = map TyVar typeParams
typeScheme = Types.Forall tyVars [] constructorType
-- Add to type environment
typeEnv' = extendEnv (stringToVar ctorName) typeScheme typeEnv
-- Add to constructor environment (for pattern matching and evaluation)
ctorInfo = ConstructorInfo
{ ctorTypeName = typeName
, ctorArgTypes = argTypes
, ctorTypeParams = typeParams
}
ctorEnv' = HashMap.insert ctorName ctorInfo ctorEnv
return (typeEnv', ctorEnv')
-- | Register a class method to the type environment
registerClassMethod :: TyVar -> String -> TypeEnv -> ClassMethod -> TypeEnv
registerClassMethod tyVar className typeEnv (ClassMethod methName params retType _defaultImpl) =
let paramTypes = map typedParamToType params
methodType = foldr TFun (typeExprToType retType) paramTypes
-- Method has constrained type: ClassName a => methodType
constraint = Types.Constraint className (TVar tyVar)
typeScheme = Types.Forall [tyVar] [constraint] methodType
in
extendEnv (stringToVar methName) typeScheme typeEnv
-- | Register type signatures for instance methods (generated during desugaring)
-- This prevents "Unbound variable" warnings during type inference
registerInstanceMethods :: String -> Type -> [Constraint] -> [InstanceMethod] -> ClassEnv -> TypeEnv -> TypeEnv
registerInstanceMethods className instType instConstraints methods classEnv typeEnv =
case lookupClass className classEnv of
Nothing -> typeEnv -- Class not found, skip
Just classInfo ->
-- Register each instance method
let typeEnv' = foldr (registerInstanceMethod className instType instConstraints classInfo) typeEnv methods
-- Also register the dictionary itself
-- e.g., eqCollection : {Eq a} Hash String ([a] -> [a] -> Bool)
typeName' = Types.typeConstructorName instType
dictName = lowerFirst className ++ typeName'
-- Build dictionary type: Hash String (method type)
-- All methods should have the same general shape, so we use the first one
dictValueType = case methods of
[] -> TAny
_ -> case lookup (instanceMethodName (head methods)) (Types.classMethods classInfo) of
Nothing -> TAny
Just methodType ->
let tyVar = Types.classParam classInfo
substitutedType = substituteTypeVar tyVar instType methodType
in substitutedType
dictType = THash TString dictValueType
freeVars = Set.toList (freeTyVars dictType)
dictScheme = Types.Forall freeVars instConstraints dictType
in
extendEnv (stringToVar dictName) dictScheme typeEnv'
where
instanceMethodName :: InstanceMethod -> String
instanceMethodName (InstanceMethod name _ _) = name
registerInstanceMethod :: String -> Type -> [Constraint] -> Types.ClassInfo -> InstanceMethod -> TypeEnv -> TypeEnv
registerInstanceMethod clsName instTy constraints classInfo (InstanceMethod methName _params _body) env =
-- Find the method in the class definition
case lookup methName (Types.classMethods classInfo) of
Nothing -> env -- Method not in class definition, skip
Just methodType ->
-- Substitute type variable with instance type
let tyVar = Types.classParam classInfo
substitutedType = substituteTypeVar tyVar instTy methodType
-- Generate method name using type constructor name only (no type parameters)
-- e.g., "eqCollectionEq" not "eqCollectionaEq"
typeName' = Types.typeConstructorName instTy
sanitizedName = sanitizeMethodName methName
generatedMethodName = lowerFirst clsName ++ typeName' ++ capitalizeFirst sanitizedName
-- Extract free type variables from the substituted type
freeVars = Set.toList (freeTyVars substitutedType)
-- Create type scheme with constraints from the instance context
-- e.g., {Eq a} [a] -> [a] -> Bool for instance {Eq a} Eq [a]
typeScheme = Types.Forall freeVars constraints substitutedType
in
extendEnv (stringToVar generatedMethodName) typeScheme env
-- Substitute type variable with concrete type in a type expression
substituteTypeVar :: TyVar -> Type -> Type -> Type
substituteTypeVar oldVar newType = go
where
go TInt = TInt
go TFloat = TFloat
go TBool = TBool
go TChar = TChar
go TString = TString
go (TVar v) | v == oldVar = newType
| otherwise = TVar v
go (TTuple ts) = TTuple (map go ts)
go (TCollection t) = TCollection (go t)
go (TInductive name ts) = TInductive name (map go ts)
go (TTensor t) = TTensor (go t)
go (THash k v) = THash (go k) (go v)
go (TMatcher t) = TMatcher (go t)
go (TFun t1 t2) = TFun (go t1) (go t2)
go (TIO t) = TIO (go t)
go (TIORef t) = TIORef (go t)
go TAny = TAny
-- | Extract method name from ClassMethod
extractMethodName :: ClassMethod -> String
extractMethodName (ClassMethod name _ _ _) = name
-- | Extract method name and type from ClassMethod
extractMethodWithType :: ClassMethod -> (String, Type)
extractMethodWithType (ClassMethod name params retType _) =
let paramTypes = map typedParamToType params
methodType = foldr TFun (typeExprToType retType) paramTypes
in (name, methodType)
-- | Extract class name from ConstraintExpr
extractConstraintName :: ConstraintExpr -> String
extractConstraintName (ConstraintExpr clsName _) = clsName
-- | Convert ConstraintExpr to internal Constraint
constraintToInternal :: ConstraintExpr -> Types.Constraint
constraintToInternal (ConstraintExpr clsName tyExprs) =
Types.Constraint clsName (case tyExprs of
[] -> TAny
(t:_) -> typeExprToType t)
-- | Register a single pattern constructor
registerPatternConstructor :: String -> [String] -> Type
-> PatternConstructorEnv -> PatternConstructor
-> EvalM PatternConstructorEnv
registerPatternConstructor _typeName typeParams resultType patternCtorEnv
(PatternConstructor ctorName argTypeExprs) = do
let argTypes = map typeExprToType argTypeExprs
-- Pattern constructor type: arg1 -> arg2 -> ... -> resultType (without Pattern wrapper)
patternCtorType = foldr TFun resultType argTypes
-- Quantify over type parameters
tyVars = map TyVar typeParams
typeScheme = Types.Forall tyVars [] patternCtorType
-- Add to pattern constructor environment (same format as PatternTypeEnv)
patternCtorEnv' = extendPatternEnv ctorName typeScheme patternCtorEnv
return patternCtorEnv'
-- | Convert TypedParam to Type
typedParamToType :: TypedParam -> Type
typedParamToType (TPVar _ ty) = typeExprToType ty
typedParamToType (TPInvertedVar _ ty) = typeExprToType ty
typedParamToType (TPTuple elems) = TTuple (map typedParamToType elems)
typedParamToType (TPWildcard ty) = typeExprToType ty
typedParamToType (TPUntypedVar _) = TVar (TyVar "a") -- Will be inferred
typedParamToType TPUntypedWildcard = TVar (TyVar "a") -- Will be inferred