Topics

Course Introduction

We describe what this course is about, including how the course is structured, a rough outline of the topics, and high-level remarks about the study of programming languages. We also introduce OCaml, including how to work with utop, how to compile simple programs, and how to write basic expressions.

Reading:

Beginning Ocaml

The Basics

We look at the basic constructs we need to start writing more interesting OCaml programs, and work through some examples. We start to see how the typing rules and semantics of OCaml work. We also introduce dune (which we'll be using for assignments).

Reading:

Let Expressions

We explore further the notion of expressions in OCaml (everything is an expression in OCaml). In particular, we look at let expressions as a way of using local variables. We continue to emphasize that as we introduce more programming abstractions, we have to formally present their syntax, typing, and semantics.

Unions and Products

We look at various ways of organizing data in OCaml including simple variants, data-carrying variants, tuples, records. We also introduced the notion of a pattern (which is is not the same thing as an expression, though it behaves similarly with respect to typing).

Reading:

Lists, Tail Recursion

We introduce the familiar notion of lists in OCaml, emphasizing that lists are immutable in OCaml. We also talk about tail recursion, how to write tail recursive functions, and why we might want to.

Reading:

Algebraic Data Types

We introduce algebraic data types as a way of defining recursive and parameterized data types. We look at many examples. (Note. ADTs are fundamental to functional programming, this is where we really start to see OCaml shine)

Reading:

Polymorphism

Many of the programs that we've written so far used specific types like int, string, and bool. We take a moment to discuss polymorphism more formally, and how to use polymorphism to write more general programs.

Higher Order Programming

Maps and Filters

We introduce the notion of higher-order functions, i.e., functions which take other functions as arguments. There are three common higher-order functions used in programming, we cover the first two: map and filter for lists.

Reading:

Folds

We cover the last (and most complex) of the common higher-order functions: fold_left and fold_right for lists. We also look at how higher-order functions can be useful for things other than lists.

Reading:

Modules and Monads

We introduce modules as a way of organizing OCaml code. We see at how modules can also be used to organize interfaces for data structures.

We also introduce monads as a way to more elegantly deal with options in functional code. We show how to use let* syntax in OCaml to make code easier to follow.

Reading:

Progress and Preservation

We take our last lecture before the midterm to discuss the notions of progress and preservation. These theorems express that well-typed programs are well-behaved, and hold for any good programming languages. Professor Das also takes a moment to lament about programming languages.

Parsing

Formal Grammar

We introduce the second half of the course, including the full interpretation pipeline. We then start at the beginning of the pipeline (i.e., parsing) looking at (E)BNF grammars, ambiguity, and precedence.

Reading:

Lexer/Parser Generators

We look more carefully at the difference between lexing and parsing. We then see how to use ocamllex and menhir to generate lexers and parsers for a grammar (in particular, how to deal with ambiguity and precedence). (Note. We will use these tools for every interpreter we build, so it is important to become comfortable with them)

Reading:

Formal Semantics

Operational Semantics

We introduce operational semantics (both small-step and big-step) as a way of formally specifying the behavior of executing a programming language. We give some examples of derivations for various semantics.

Reading:

The Substitution Model

We look the lambda calculus, its syntax and semantics. We also discuss capture-avoiding substitution as a way of dealing with some of the more subtle details of evaluating expressions in the lambda calculus.

Reading:

Variables, Scope, Closures

We discuss (dynamic and lexical) scoping, with an emphasis on how variable scoping is dealt with in OCaml. We consider how this connects to call-by-value semantics and closures.

Reading:

Closures and the Environment Model

We further discuss closures as a way to better implement lexical scoping in functional programming languages. We introduce the environment model as a more efficient alternative to the substitution model from the previous week.

Reading:

Type Checking

Introduction

We look even more carefully at the notion of a typing derivation.

In Practice

We look at the practical considerations of type checking.

Reading:

Type Inference

Introduction

We discuss type inference and let-polymorphism (potentially non-polymorphic lets).

Unification

We discuss more generally the notion of unification, which underlies the type inference algorithm we will be implementing.

In Practice

We look at practical considerations in the implementation of a type inference algorithm.

Reading:

Compilation

Stack-Based Languages

We discuss stack-oriented languages in general and introduce the syntax and semantics of a basic stack-oriented language with procedures.

In Practice

We give a high-level introduction of compilation and its relation to interpretation and work through some simple examples. We then go through an example of building a simple byte-code interpreter.

Advanced Topic: TBA

In our last lecture, we cover an advanced topic based on the 300-400 level PL courses being offered in the spring.