------------------------------------------------------------------------------ --- Library to annotate the expressions of a FlatCurry program --- with type information. --- --- It can be used by any other Curry program which processes --- or transforms FlatCurry programs. The main operation to use is --- --- inferProg :: Prog -> IO (Either String (AProg TypeExpr)) --- --- which annotates a FlatCurry program with type information. --- --- The type inference works in several steps: --- --- 1. For each known function and constructor, either imported or defined --- in the module itself, the respective type is inserted into a type --- environment (type assumption). --- --- 2. Every subexpression is annotated with a fresh type variable, whereas --- constructor and function names are annotated with a fresh variant of --- the type in the type assumption. --- --- 3. Based on FlatCurry's type inference rules, type equations are generated --- for a function's rule. --- --- 4. The resulting equations are solved using unification and the --- resulting substitution is applied to the function rule. --- --- 5. The inferred types are then normalized such that for every function --- rule the type variables start with 0. --- --- In addition, the function `inferNewFunctions` allows to infer the types --- of a list of functions whose type is not known before. Consequently, --- this disallows polymorphic recursive functions. Those functions are --- separated into strongly connected components before their types are inferred --- to allow mutually recursive function definitions. --- --- In case of any error, the type inference quits with an error message. --- --- IMPORTANT NOTE: The type inference is incomplete w.r.t. type classes --- and forall types. This need to be extended in the future! --- --- @author Jonas Oberschweiber, Bjoern Peemoeller, Michael Hanus --- @version November 2025 ------------------------------------------------------------------------------ module FlatCurry.TypeAnnotated.TypeInference ( TypeEnv, getTypeEnv, getTypeEnvFromProgEnv , inferProg, inferProgFromProgEnv, inferProgEnv , inferFunction, inferFunctionEnv , inferNewFunctions, inferNewFunctionsEnv , inferExpr, inferExprEnv ) where import Control.Monad.Extra ( concatMapM, mapAccumM ) import Control.Monad.Trans.Class ( lift ) import Control.Monad.Trans.State import Control.Monad.Trans.Except import Control.Applicative import qualified Data.Map as Map import Data.List ( find ) import Data.SCC import FlatCurry.Types import FlatCurry.Files import FlatCurry.Goodies ( branchExpr, funcName ) import FlatCurry.Annotated.Types import qualified FlatCurry.Annotated.Goodies as AFC ( annExpr, funcName ) import FlatCurry.Annotated.Pretty ( ppQName, ppExp, ppTypeExp , ppVarIndex) import FlatCurry.TypeAnnotated.TypeSubst import qualified Text.Pretty as P import Rewriting.Term import Rewriting.Unification -- --------------------------------------------------------------------------- -- Public Interface -- --------------------------------------------------------------------------- --- Infers the type of a whole program. --- --- @param p - the Prog to infer --- @return the inferred program or an error inferProg :: Prog -> IO (Either String (AProg TypeExpr)) inferProg p = getTypeEnv p >>= \te -> return (inferProgEnv te p) --- Infers the type of a whole program w.r.t. a list of imported modules. --- --- @param p - the Prog to infer --- @return the inferred program or an error inferProgFromProgEnv :: [(String, Prog)] -> Prog -> Either String (AProg TypeExpr) inferProgFromProgEnv env p = case getTypeEnvFromProgEnv env p of Left err -> Left err Right tyEnv -> inferProgEnv tyEnv p --- Infers the types of a single function specified by its qualified name. --- --- @param p - the Prog containing the function --- @param q - the qualified name of the function --- @return the inferred function or an error inferFunction :: Prog -> QName -> IO (Either String (AFuncDecl TypeExpr)) inferFunction p f = getTypeEnv p >>= \te -> return (inferFunctionEnv te p f) --- Infers the types of a group of (possibly mutually recursive) functions. --- Note that the functions are only monomorphically instantiated, i.e., --- polymorphic recursion is not supported. --- The given type may be too general, for instance a type variable only, --- and will be specialised to the inferred type. inferNewFunctions :: Prog -> [FuncDecl] -> IO (Either String [AFuncDecl TypeExpr]) inferNewFunctions p@(Prog mid _ _ _ _) fs = getTypeEnv p >>= \te -> return (inferNewFunctionsEnv te mid fs) --- Infer the type of a single expression. --- --- @param p - the Prog containing the function --- @param e - the expression --- @return the inferred expression or an error inferExpr :: Prog -> Expr -> IO (Either String (AExpr TypeExpr)) inferExpr p e = getTypeEnv p >>= \te -> return (inferExprEnv te e) -- --------------------------------------------------------------------------- --- Infers the type of a whole program. --- Uses the given type environment instead of generating a new one. --- --- @param te - the type environment --- @param p - the Prog to infer --- @return the inferred program or an error inferProgEnv :: TypeEnv -> Prog -> Either String (AProg TypeExpr) inferProgEnv te p = evalErrorState (annProg p >>= inferAProg) (initTIM te) --- Infers the types of a single function specified by its qualified name. --- Uses the given type environment instead of generating a new one. --- --- @param te - the type environment --- @param p - the Prog containing the function --- @param fun - the qualified name of the function --- @return the inferred function or an error inferFunctionEnv :: TypeEnv -> Prog -> QName -> Either String (AFuncDecl TypeExpr) inferFunctionEnv te (Prog _ _ _ fs _) f = case find ((== f) . funcName) fs of Nothing -> Left $ P.showWidth 80 $ P.text "No such function:" P.<+> ppQName f Just fd -> evalErrorState (annFunc fd >>= inferFunc) (initTIM te) --- Infers the types of a group of (possibly mutually recursive) functions. --- Note that the functions are only monomorphically instantiated, i.e., --- polymorphic recursion is not supported. --- The given type may be too general, for instance a type variable only, --- and will be specialised to the inferred type. inferNewFunctionsEnv :: TypeEnv -> String -> [FuncDecl] -> Either String [AFuncDecl TypeExpr] inferNewFunctionsEnv te mid fs = evalErrorState (infer (depGraph mid fs)) (initTIM te) where infer fss = concatMapM inferGroup fss >>= \fs' -> mapM (flip extract fs') fs inferGroup g = annFuncGroup g >>= inferFuncGroup >>= \afs -> extendTypeEnv [(f, ty) | AFunc f _ _ ty _ <- afs] >> return afs extract f afs = case find ((== funcName f) . AFC.funcName) afs of Just af -> return af Nothing -> throwE "Internal error: extract" --- Infers the types of a single expression. --- Uses the given type environment instead of generating a new one. --- --- @param te - the type environment --- @param e - the expression --- @return the inferred expression or an error inferExprEnv :: TypeEnv -> Expr -> Either String (AExpr TypeExpr) inferExprEnv te e = evalErrorState (annExpr e >>= inferAExpr) (initTIM te) evalErrorState :: ExceptT e (State s) a -> s -> Either e a evalErrorState es s = evalState (runExceptT es) s -- --------------------------------------------------------------------------- -- Computation of dependency graph and strongly connected components -- --------------------------------------------------------------------------- --- Compute the dependency graph of functions and separate it into strongly --- connected components. depGraph :: String -> [FuncDecl] -> [[FuncDecl]] depGraph mid = scc def use where def (Func f _ _ _ _) = [f] use (Func _ _ _ _ r) = case r of Rule _ e -> called e _ -> [] called (Var _) = [] called (Lit _) = [] called (Comb ct f@(m, _) es) | m == mid = (case ct of FuncCall -> [f] FuncPartCall _ -> [f] _ -> []) ++ concatMap called es | otherwise = concatMap called es called (Let bs e) = concatMap called (expsOfLetBind bs ++ [e]) called (Free _ e) = called e called (Or a b) = concatMap called [a, b] called (Case _ e bs) = concatMap called (e : map branchExpr bs) called (Typed e _) = called e -- --------------------------------------------------------------------------- -- 1. Type environment -- --------------------------------------------------------------------------- --- A type environment. type TypeEnv = Map.Map QName TypeExpr --- Looks up a type with a qualified name in a type environment. --- --- @param env - the type environment --- @param q - the qualified name to look for --- @return maybe the type lookupType :: TypeEnv -> QName -> Maybe TypeExpr lookupType = flip Map.lookup --- Extract the type environment from the given Prog. --- --- @param p - the Prog --- @return a type environment getTypeEnv :: Prog -> IO TypeEnv getTypeEnv p = do is <- extractImported p return (extractKnownTypes $ p : is) --- Reads the interfaces of all modules imported into the given Prog. --- --- @param p - the Prog whose imports should be read --- @return the list of interface Progs extractImported :: Prog -> IO [Prog] extractImported (Prog _ is _ _ _) = mapM readFlatCurryInt is --- Extract the type environment from the given Prog by lookup in a --- module name -> Prog environment. --- --- @param env - An environment mapping module names to Progs --- @param p - the Prog --- @return a type environment getTypeEnvFromProgEnv :: [(String, Prog)] -> Prog -> Either String TypeEnv getTypeEnvFromProgEnv env prog@(Prog _ imps _ _ _) = case extract imps of Left err -> Left err Right mods -> Right (extractKnownTypes $ prog : mods) where extract [] = Right [] extract (i:is) = case lookup i env of Nothing -> Left $ "getTypeEnvFromProgEnv: Could not find module " ++ i Just p -> case extract is of Left err -> Left err Right ps -> Right (p : ps) --- Extracts the type information of all function and datatype --- declarations from the given list of Progs. --- --- @param ps - the list of Progs --- @return a type environment extractKnownTypes :: [Prog] -> TypeEnv extractKnownTypes ps = Map.fromList $ concatMap extractProg ps where extractProg :: Prog -> [(QName, TypeExpr)] extractProg (Prog _ _ td fd _) = concatMap extractTypeDecl td ++ map extractFuncDecl fd extractFuncDecl :: FuncDecl -> (QName, TypeExpr) extractFuncDecl (Func n _ _ ty _) = (n, ty) extractTypeDecl :: TypeDecl -> [(QName, TypeExpr)] extractTypeDecl (TypeSyn n _ _ ty) = [(n, ty)] extractTypeDecl (TypeNew n _ vs c) = pure $ extractNewConsDecl ty c where ty = TCons n (map (TVar . fst) vs) extractTypeDecl (Type n _ vs cs) = map (extractConsDecl ty) cs where ty = TCons n (map (TVar . fst) vs) extractConsDecl :: TypeExpr -> ConsDecl -> (QName, TypeExpr) extractConsDecl ty (Cons n _ _ tys) = (n, foldr FuncType ty tys) extractNewConsDecl :: TypeExpr -> NewConsDecl -> (QName, TypeExpr) extractNewConsDecl ty (NewCons n _ ty') = (n, FuncType ty' ty) typeArity :: TypeExpr -> Int typeArity ty = case ty of FuncType _ b -> 1 + typeArity b _ -> 0 -- --------------------------------------------------------------------------- -- Type Inference Monad -- --------------------------------------------------------------------------- --- The monad contains an `Int` value for fresh type variable generation --- and a mapping from variable indices to their associated type --- variables. It returns a `String` if an error occured. type TIS = (TypeEnv, Int, TypeEnv, Map.Map Int TypeExpr) type TIM = ExceptT String (State TIS) --- Initial TIM state. initTIM :: TypeEnv -> TIS initTIM te = (te, 0, Map.empty, Map.empty) extendTypeEnv :: [(QName, TypeExpr)] -> TIM () extendTypeEnv ftys = lift (get >>= \ (te, v, fe, ve) -> put (Map.insertList ftys te, v, fe, ve)) --- Retrieve the next fresh type variable. nextTVar :: TIM TypeExpr nextTVar = lift (get >>= \ (te, n, fun2Ty, var2Ty) -> put (te, n + 1, fun2Ty, var2Ty) >> return (TVar n)) --- Intialize the mapping from variables to type variables. initVarTypes :: TIM () initVarTypes = lift $ modify $ \ (te, n, fe, _) -> (te, n, fe, Map.empty) --- Intialize the type signature environment, i.e., delete all associations. initSigEnv :: TIM () initSigEnv = lift $ modify $ \ (te, n, _, ve) -> (te, n, Map.empty, ve) --- Insert a new variable/type variable association. insertVarType :: Int -> TypeExpr -> TIM () insertVarType v ty = lift $ modify $ \ (te, n, fe, var2Ty) -> (te, n, fe, Map.insert v ty var2Ty) --- Insert a new function/type signature association. insertFunType :: QName -> TypeExpr -> TIM () insertFunType f sig = freshVariant sig >>= \ty -> lift $ modify $ \ (te, n, fe, ve) -> (te, n, Map.insert f ty fe, ve) --- Look up the type variable associated to a variable. lookupVarType :: Int -> TIM (Maybe TypeExpr) lookupVarType v = lift (get >>= \ (_, _, _, var2Ty) -> return (Map.lookup v var2Ty)) --- Looks up a type in the type assumption or the type signature environment. --- Types found in the type assumption are instantiated to a fresh variant. --- --- @param q - the qualified name of the type to look up --- @return its type getTypeVariant :: QName -> TIM (QName, TypeExpr) getTypeVariant f = lift get >>= \ (env, _, fe, _) -> case lookupType env f of Nothing -> case Map.lookup f fe of Nothing -> throwE $ "Internal error: getTypeVariant " ++ show f Just ty -> return (f, ty) Just t -> freshVariant t >>= \ty -> return (f, ty) --- Compute a fresh variant of a given type expression. --- --- @param ty - the type expression --- @return The renamed type expression freshVariant :: TypeExpr -> TIM TypeExpr freshVariant ty = snd <$> rename [] ty where rename ren (TVar i) = case lookup i ren of Just j -> return (ren, j) Nothing -> nextTVar >>= \j -> return ((i, j) : ren, j) rename ren (FuncType a b) = rename ren a >>= \ (ren1, a') -> rename ren1 b >>= \ (ren2, b') -> return (ren2, FuncType a' b') rename ren (TCons t tys) = mapAccumM rename ren tys >>= \(ren', tys') -> return (ren', TCons t tys') rename _ (ForallType _ _) = error $ "FlatCurry.Annotated.TypeInference.freshVariant: " ++ "ForallType not yet supported!" -- ----------------------------------------------------------------------------- -- 2. Annotation, traversing the AST and inserting fresh type variables -- ----------------------------------------------------------------------------- --- Converts the Prog to an AProg, inserting TVars into all expressions. --- --- @param prog - the prog to convert --- @return an AProg and the next TVar number in an TIM annProg :: Prog -> TIM (AProg TypeExpr) annProg (Prog mid is td fd od) = (\afd -> AProg mid is td afd od) <$> mapM annFunc fd annFuncGroup :: [FuncDecl] -> TIM [AFuncDecl TypeExpr] annFuncGroup fs = initSigEnv >> mapM (uncurry insertFunType) ftys >> mapM annFunc fs where ftys = [ (f, ty) | Func f _ _ ty _ <- fs] --- Converts the FuncDecl to an AFuncDecl, inserting TVars into all --- expressions. annFunc ::FuncDecl -> TIM (AFuncDecl TypeExpr) annFunc (Func qn a v _ r) = initVarTypes >> AFunc qn a v <$> (snd <$> getTypeVariant qn) <*> annRule r --- Converts the Rule to an ARule, inserting TVars into all expressions. annRule :: Rule -> TIM (ARule TypeExpr) annRule (Rule vs e) = ARule <$> nextTVar <*> mapM annVar vs <*> annExpr e annRule (External s) = flip AExternal s <$> nextTVar --- Converts the Expr to an AExpr, inserting TVars into all sub-expressions. annExpr :: Expr -> TIM (AExpr TypeExpr) annExpr (Var i) = lookupVarType i >>= maybe (throwE err) (\ty -> return (AVar ty i)) where err = P.showWidth 80 $ P.text "Variable" P.<+> ppVarIndex i P.<+> P.text "was not initialized with a type" annExpr (Lit l) = nextTVar >>= \ty -> return (ALit ty l) annExpr (Comb t q es) = flip AComb t <$> nextTVar <*> getTypeVariant q <*> mapM annExpr es annExpr (Case t e bs) = flip ACase t <$> nextTVar <*> annExpr e <*> mapM annBranch bs annExpr (Or a b) = AOr <$> nextTVar <*> annExpr a <*> annExpr b annExpr (Let ds e) = ALet <$> nextTVar <*> annBindings ds <*> annExpr e where annBindings bs = let (vs, _, es) = unzip3 bs in mapM annBound vs >>= \vs' -> mapM annExpr es >>= \es' -> return (zip vs' es') annBound v = checkShadowing v >> annVar v annExpr (Free vs e) = AFree <$> nextTVar <*> mapM annFree vs <*> annExpr e where annFree (v,_) = checkShadowing v >> annVar v annExpr (Typed e ty) = ATyped <$> nextTVar <*> annExpr e <*> freshVariant ty --- Annotate a variable with a fresh type variable. annVar :: VarIndex -> TIM (VarIndex, TypeExpr) annVar v = nextTVar >>= \ty -> insertVarType v ty >> return (v, ty) --- Checks whether a locally introduced variable is already defined in the --- surrounding scope, which indicates variable shadowing that is not allowed --- in FlatCurry files. This is our basic assumption in this type inferencer, --- and must therefore be met. Otherwise, the type inference must be extended. checkShadowing :: VarIndex -> TIM () checkShadowing v = lookupVarType v >>= maybe (return ()) (\_ -> throwE err) where err = P.showWidth 80 $ P.text "shadowing with variable" P.<+> ppVarIndex v --- Converts the BranchExpr to an ABranchExpr, inserting TVars --- into all expressions --- --- @param n - the first TVar number to use --- @return the ABranchExpr and the new next TVar number in an TIM annBranch :: BranchExpr -> TIM (ABranchExpr TypeExpr) annBranch (Branch p e) = ABranch <$> annPattern p <*> annExpr e --- Converts the Pattern into an APattern, inserting a TVar --- into the pattern --- --- @param n - the TVar number to use --- @return the APattern and the new next TVar number in an TIM annPattern :: Pattern -> TIM (APattern TypeExpr) annPattern (Pattern c vs) = APattern <$> nextTVar <*> getTypeVariant c <*> mapM annPVar vs where annPVar v = checkShadowing v >> annVar v annPattern (LPattern l) = flip ALPattern l <$> nextTVar -- --------------------------------------------------------------------------- -- 3. Type inference -- --------------------------------------------------------------------------- --- Type equations type TypeEqs = [(TypeExpr, TypeExpr)] --- Smart constructor for type equation (=.=) :: TypeExpr -> TypeExpr -> (TypeExpr, TypeExpr) ty1 =.= ty2 = (ty1, ty2) ppTypeEqs :: TypeEqs -> P.Doc ppTypeEqs = P.vsep . map ppEquation where ppEquation (l, r) = ppTypeExp l P.<+> P.equals P.<+> ppTypeExp r --- Append two lists yielded by monadic computations. (++=) :: TIM [a] -> TIM [a] -> TIM [a] mxs ++= mys = (++) <$> mxs <*> mys --- Infers all types in the given program. --- --- @param p - the program to infer --- @param n - the next fresh TVar number --- @return the inferred program or an error inferAProg :: AProg TypeExpr -> TIM (AProg TypeExpr) inferAProg (AProg mid is td fd od) = (\fd' -> AProg mid is td fd' od) <$> mapM inferFunc fd --- Infers all types in the given function declaration group. inferFuncGroup :: [AFuncDecl TypeExpr] -> TIM [AFuncDecl TypeExpr] inferFuncGroup fs = -- return (unsafePerformIO (mapIO_ print fs)) >>= \() -> concatMapM (uncurry eqsRule) [(ty, r) | AFunc _ _ _ ty r <- fs] >>= \eqs -> -- return (unsafePerformIO (putStrLn (ppTypeEqs eqs))) >>= \() -> solve (P.text "functions" P.<+> doc) eqs >>= \ sigma -> -- return (unsafePerformIO (putStrLn (showAFCSubst sigma))) >>= \() -> mapM (normalize normFunc . substFunc sigma) fs >>= \afs -> -- return (unsafePerformIO (mapIO_ print afs)) >>= \() -> return afs where doc = P.hsep $ P.punctuate P.comma (map (ppQName . AFC.funcName) fs) --- Infers all types in the given function. --- --- @param n - the next fresh TVar number --- @param f - the function --- @return the inferred function or an error inferFunc :: AFuncDecl TypeExpr -> TIM (AFuncDecl TypeExpr) inferFunc func@(AFunc _ _ _ ty r) = eqsRule ty r >>= \ eqs -> solve (P.text "function" P.<+> ppQName (AFC.funcName func)) eqs >>= \ sigma -> normalize normFunc (substFunc sigma func) --- Infer the type of an expression. inferAExpr :: AExpr TypeExpr -> TIM (AExpr TypeExpr) inferAExpr e = eqsExpr e >>= \eqs -> solve (P.text "expression" P.<+> ppExp e) eqs >>= \sigma -> normalize normExpr (substExpr sigma e) --- Infer the type for a rule. eqsRule :: TypeExpr -> ARule TypeExpr -> TIM TypeEqs eqsRule ty (ARule ty2 vs e) = return [ty =.= ty2, ty2 =.= foldr1 FuncType (map snd vs ++ [exprType e])] ++= eqsExpr e eqsRule ty (AExternal ty2 _) = return [ty =.= ty2] --- Recursively generate equations for unification from an expression. --- --- @param e - the expression --- @return a list of type equations generated from `e`. eqsExpr :: AExpr TypeExpr -> TIM TypeEqs -- No equations to generate. eqsExpr (AVar _ _) = return [] -- The type of the expression is equal to the type of the literal. eqsExpr (ALit ty l) = return [ty =.= literalType l] -- Match the types of the argument expressions and the result type -- to the type of the function or constructor. eqsExpr (AComb ty _ (_, fty) es) = return [fty =.= foldr1 FuncType (map exprType es ++ [ty])] ++= concatMapM eqsExpr es -- Generate equations for the subject and the branches. eqsExpr (ACase ty _ e bs) = eqsExpr e ++= concatMapM (eqsBranch ty e) bs -- The type of the expression must be equal to the types -- of both argument expressions. eqsExpr (AOr ty a b) = return [exprType a =.= ty, exprType b =.= ty] ++= eqsExpr a ++= eqsExpr b -- The type of the expression must be equal to the type of the inner expression. -- The type of each bound variable must be equal to the type of the -- corresponding expression. eqsExpr (ALet ty bs e) = return [ty =.= exprType e] ++= return (map (\ ((_, vty), b) -> vty =.= exprType b) bs) ++= concatMapM eqsExpr (e : map snd bs) -- The type of the expression itself must be equal to the type -- of the inner expression. eqsExpr (AFree ty _ e) = return [ty =.= exprType e] ++= eqsExpr e -- The type of the expression must be equal to the type of the argument -- expression and to the specified type. eqsExpr (ATyped ty e tz) = return [ty =.= exprType e, ty =.= tz] ++= eqsExpr e --- Generate equations for a branch. --- --- - equating the type of the branch's expression to the type of the --- overall case expression --- - generating equations for the branch's pattern --- - generating equations for the branch's expression --- --- @param ty - the parent case expression's type --- @param subj - the case's subject expression --- @param b - the branch eqsBranch :: TypeExpr -> AExpr TypeExpr -> ABranchExpr TypeExpr -> TIM TypeEqs eqsBranch ty s (ABranch p be) = return [ty =.= exprType be] ++= eqsPattern (exprType s) p ++= eqsExpr be --- Generate equations for a pattern. --- --- - Equate the type of the case's subject to the type of the pattern. --- - For constructor patterns: Equate the type of the argument patterns and --- the type of the subject to the type of the constructor. --- - For literal patterns: Equate the type of the pattern to the type --- of the literal. eqsPattern :: TypeExpr -> APattern TypeExpr -> TIM TypeEqs eqsPattern ty (APattern pty (_, cty) vs) = return [ty =.= pty, cty =.= foldr1 FuncType (map snd vs ++ [pty])] eqsPattern ty (ALPattern pty l) = return [ty =.= pty, pty =.= literalType l] --- Extract the type of a Literal. literalType :: Literal -> TypeExpr literalType (Intc _) = TCons ("Prelude", "Int" ) [] literalType (Floatc _) = TCons ("Prelude", "Float") [] literalType (Charc _) = TCons ("Prelude", "Char" ) [] --- Extract the TypeExpr from an annotated Expr. exprType :: AExpr TypeExpr -> TypeExpr exprType = AFC.annExpr -- --------------------------------------------------------------------------- -- 4. Functions for interfacing with the Unification module -- --------------------------------------------------------------------------- --- Solve a list of type equations using unification. solve :: P.Doc -> TypeEqs -> TIM AFCSubst solve what eqs = case unify (fromTypeEqs eqs) of Left err -> throwE $ P.showWidth 80 $ ppUnificationError err P.<+> P.text "during type inference for:" P.<$$> P.nest 2 what Right sub -> return (Map.mapWithKey (\_ -> toTypeExpr) sub) --- Converts a list of type expression equations into term equations. fromTypeEqs :: TypeEqs -> TermEqs String fromTypeEqs = map (\(a,b) -> (fromTypeExpr a, fromTypeExpr b)) --- Converts a list of term equations into type expression equations. toTypeEqs :: TermEqs String -> TypeEqs toTypeEqs = map (\(a,b) -> (toTypeExpr a =.= toTypeExpr b)) --- Converts the given type expression into a term for unification. fromTypeExpr :: TypeExpr -> Term String fromTypeExpr (TVar n) = TermVar n fromTypeExpr (TCons t vs) = TermCons (fromQName t) (map fromTypeExpr vs) fromTypeExpr (FuncType a b) = TermCons "->" [fromTypeExpr a, fromTypeExpr b] fromTypeExpr (ForallType _ _) = error $ "FlatCurry.Annotated.TypeInference.fromTypeExpr: " ++ "ForallType not yet supported!" --- Converts the given unification term into a type expression toTypeExpr :: Term String -> TypeExpr toTypeExpr (TermVar n) = TVar n toTypeExpr (TermCons t vs) | t == "->" = FuncType (toTypeExpr (vs !! 0)) (toTypeExpr (vs !! 1)) | otherwise = TCons (toQName t) (map toTypeExpr vs) --- Converts a qualified name to a string. fromQName :: QName -> String fromQName (mod, typ) = mod ++ ";" ++ typ --- Converts a string to a qualified name. toQName :: String -> QName toQName str = (fst split, snd split) where split = splitFirst str ';' --- Splits a list at the first occurence of a given value. --- --- @param xs - the list to split --- @param x - the value to split at --- @return a tuple of the lists before and after the split splitFirst :: Eq a => [a] -> a -> ([a], [a]) splitFirst [] _ = ([], []) splitFirst (x:xs) c | x == c = ([], xs) | otherwise = (x : fst rest, snd rest) where rest = splitFirst xs c --- Formats a unification error with the given message. ppUnificationError :: UnificationError String -> P.Doc ppUnificationError (Clash a b) = P.text "Clash:" P.<+> ppTypeExp (toTypeExpr a) P.<+> P.equals P.<+> ppTypeExp (toTypeExpr b) ppUnificationError (OccurCheck v t) = P.text "OccurCheck: Type variable" P.<+> ppTypeExp (toTypeExpr (TermVar v)) P.<+> P.text "occurs in type" P.<+> ppTypeExp (toTypeExpr t) -- --------------------------------------------------------------------------- -- 5. Functions for normalization of type variables. -- Renumbers type variables in a function starting from 0. -- --------------------------------------------------------------------------- -- We need to keep the next fresh variable number to assign and a mapping from -- existing variable numbers to newly assigned ones. type NormState = (Int, Map.Map Int Int) type Normalize a = a -> State NormState a --- Run a normalization operation. normalize :: (Monad m, Show a) => Normalize a -> a -> m a normalize norm x = return $ evalState (norm x) (0, Map.empty) --- Normalizes the type variable numbers in the given function. --- The parameters of the function are always the first types to be --- renumbered so they are assigned the lowest numbers. --- --- @param func - the function to normalize --- @return the normalized function normFunc :: Normalize (AFuncDecl TypeExpr) normFunc (AFunc f a v t r) = AFunc f a v <$> normType t <*> normRule r --- Recursively normalizes type variable numbers in the given type expression. --- --- @param type - the type expression to normalize --- @return the normalized type expression normType :: Normalize TypeExpr normType (TVar i) = get >>= \(n, fm) -> case Map.lookup i fm of Nothing -> put (n + 1, Map.insert i n fm) >> return (TVar n) Just n' -> return (TVar n') normType (TCons q tys) = TCons q <$> mapM normType tys normType (FuncType a b) = FuncType <$> normType a <*> normType b normType (ForallType _ _) = error $ "FlatCurry.Annotated.TypeInference.normType: " ++ "ForallType not yet supported!" --- Normalize a rule. normRule :: Normalize (ARule TypeExpr) normRule (ARule ty vs e) = ARule <$> normType ty <*> mapM normSnd vs <*> normExpr e normRule (AExternal ty s) = flip AExternal s <$> normType ty --- Normalizes type variable numbers in an expression. The next number --- to assign and a map from existing variable numbers to newly assigned --- ones are managed using the state monad. --- --- @param state - the current state --- @param expr - the expression to normalize --- @return the new state and normalized expression inside the state monad normExpr :: Normalize (AExpr TypeExpr) normExpr (AVar t v) = flip AVar v <$> normType t normExpr (ALit t l) = flip ALit l <$> normType t normExpr (AComb t ct f es) = flip AComb ct <$> normType t <*> normSnd f <*> mapM normExpr es normExpr (ALet t ds e) = ALet <$> normType t <*> mapM normBinding ds <*> normExpr e where normBinding (v, b) = (\x y -> (x,y)) <$> normSnd v <*> normExpr b normExpr (AOr t a b) = AOr <$> normType t <*> normExpr a <*> normExpr b normExpr (ACase t ct e bs) = flip ACase ct <$> normType t <*> normExpr e <*> mapM normBranch bs normExpr (AFree t vs e) = AFree <$> normType t <*> mapM normSnd vs <*> normExpr e normExpr (ATyped t e y) = ATyped <$> normType t <*> normExpr e <*> normType y normSnd :: Normalize (a, TypeExpr) normSnd (a, ty) = normType ty >>= \ty' -> return (a, ty') --- Normalizes type variable numbers in a branch. State is managed --- using the state monad, see normExpr for details. --- --- @param state - the current state --- @param branch - the branch to normalize --- @return the new state and normalized branch inside the state monad normBranch :: Normalize (ABranchExpr TypeExpr) normBranch (ABranch p e) = ABranch <$> normPattern p <*> normExpr e normPattern :: Normalize (APattern TypeExpr) normPattern (APattern t c vs) = APattern <$> normType t <*> normSnd c <*> mapM normSnd vs normPattern (ALPattern t l) = flip ALPattern l <$> normType t