15-150: Principles of Functional Programming

Lecture 4: Structural Induction and Tail Recursive

In the previous lecture, we introduced SML's predefined datatype   int list   for lists of integers.
An important point is that the list datatype is defined recursively:
An   int list   is one of the following (and nothing else):

  1. Either the empty list (written nil or []),
  2. or of the form  x::xs, with   x : int   and   xs : int list.

Last time, we introduced the SML type of lists, such as int list, which describes a (possibly-empty) collection of integers. We saw how we can have lists of all types, with the mandate that all lists must have elements that have the same type.

Today, we discussed the difference between constructing and deconstructing data. We saw how we can use the "cons" operator :: to prepend an element to a list, as well as in a pattern to extract the data out from a list. We saw how we can use a case expression in much the same way as how we cased upon tuples and integers.

Using our new list data structures, we implemented some common functions on lists and discussed how they admit a new kind of induction called structural induction, which lets us induct directly on the list value itself, instead of just on a related quantity like the length of the list.

We then discussed tail recursion, a form of recursion where no additional work is done after the recursive call. This form of recursion is special because it can be optimized to be more efficient. We saw that these functions usually are made tail-recursive by taking in an accumulator argument, which stores the answer until it is meant to finally be returned.

Slides