## Main Type Inference Function

Now, we are ready to pin down the type inference function `ti`

which infers the types for expressions.

`> ti :: (MonadState TIState m, MonadError String m) => `

> TypeEnv -> Exp -> m (Subst, Type)

The function expects the precondition that the type environment *must* contain bindings for all free variables of the expressions. The output action is a state-and-error monad `m`

containing a pair of a substitution which records the type constraints imposed on type variables by the expression, and the inferred type of the expression.

First, the easiest cases are the literals whose *base* types are trivially inferred.

`> ti env (ELit (LInt _)) = return (empSubst, TInt)`

> ti env (ELit (LBool _)) = return (empSubst, TInt)

Next, consider the case of variables. Here we simply lookup the environment for the scheme for the variable, and if found, create a fresh instantiation for this particular usage of the variable.

`> ti (TypeEnv env) (EVar x) = `

> case Map.lookup x env of

> Nothing -> throwError $ "unbound variable: " ++ show x

> Just s -> instantiate s >>= return . (,) empSubst

For the `EAbs x e`

case (ie function definition) we assign the formal `x`

a fresh type variable and analyze the body `e`

using that type variable. Note how the substitution from the body is applied to the type variable for the formal to obtain the inferred *function* type.

`> ti env (EAbs x e) =`

> do tv <- freshTVar "a"

> let env' = env \\ (x, Forall [] tv)

> (s1, t1) <- ti env' e

> return (s1, (apply s1 tv) `TArr` t1)

Next, notice how in the `EApp e1 e2`

case the recorded substitutions for `e1`

are applied to the environment before `e2`

is analyzed, and the substitutions from `e2`

are composed with those for `e1`

to yield the substitution for the entire expression.

`> ti env (EApp e1 e2) =`

> do tv <- freshTVar "a"

> (s1, t1) <- ti env e1

> (s2, t2) <- ti (apply s1 env) e2

> s3 <- mgu (apply s2 t1) (TArr t2 tv)

> return (s3 `after` s2 `after` s1, apply s3 tv)

Finally, for the case of a let-binding `ELet x e1 e2`

we infer the type for `e1`

and then *generalize* it to obtain the scheme bound to `x`

when analyzing `e2`

.

`> ti env (ELet x e1 e2) =`

> do (s1, t1) <- ti env e1

> let t' = generalize (apply s1 env) t1

> env' = env \\ (x, t')

> (s2, t2) <- ti (apply s1 env') e2

> return (s1 `after` s2, t2)

Finally, we use the `ti`

function (which returns a type inference action) to define the `typeInference`

function promised at the beginning.

`> -- type TI a = ErrorT String (State TIState) a`

`> typeInference env e = uncurry apply <$> res`

> where act = ti env e

> res = evalState (runErrorT act) s0

> s0 = TIState { count = 0 }