Evaluation
Free-variable analysis, substitution, and call-by-value function application.
From evaluating expressions to reasoning about their types: implementing the core algorithms of a small functional language in OCaml.
The project
Across a sequence of assignments, I implemented the core evaluation and type-analysis algorithms for λ⁺. The work progressed from substitution-based evaluation to checking annotated programs and inferring types through constraints.
01 / Implementation scope
The course supplied the AST, parser, supporting utilities, and interactive interface. Within that framework, I implemented the following core algorithms.
Free-variable analysis, substitution, and call-by-value function application.
Arithmetic, conditionals, lists, pattern matching, and recursive definitions.
Environment-based type checking and constraint-based type inference.
Type substitution and unification with an occurs check.
Additional work: simultaneous substitution and alpha-equivalence checking.
02 / Runtime
The interpreter recursively evaluates the abstract syntax tree. For function application, it evaluates the function and argument, substitutes the resulting argument value into the function body, and evaluates that body.
Free-variable analysis distinguishes references from names introduced by functions, let expressions, and pattern matching. Substitution stops beneath a binding that shadows the variable being replaced.
Lists, conditional execution, and recursion through a fixed-point operator enable recursive computations within the same evaluation model.
03 / Static analysis
The type checker translates typing rules into recursive checks. The inference engine introduces fresh type variables for missing annotations, generates equality constraints, and solves them through unification.
Function application
λf. λx. f xAssign f the fresh type α and x the fresh type β.
Give the application f x a fresh result type γ. Applying f to x requires α = β → γ.
Solve the equation with the substitution α ↦ β → γ, then apply it to the provisional type α → β → γ.
The resulting type is (β → γ) → β → γ.
In λx. x x, the same x acts as both function and argument. With x : α and a fresh result type β, the application requires α = α → β. The occurs check rejects this constraint because α appears within its own replacement, which would require an infinite type.
04 / Reflection
Evaluation rules became recursive interpreter cases; typing judgments became environment-based checks; and unknown types became constraints that could be solved systematically.
The project deepened my understanding of how bindings determine variable scope, how expressions evaluate at runtime, and how static analysis reasons about a program before execution.