Abstract. In these lecture notes we give an introduction to functional programming with dependent types. We use the dependently typed programming language Agda which is an extension of Martin-L¨ of type theory. First we show how to do simply typed functional programming in the style of Haskell and ML. Some differences between Agda’s type system and the Hindley-Milner type system of Haskell and ML are also discussed. Then we show how to use dependent types for programming and we explain the basic ideas behind type-checking dependent types. We go on to explain the Curry-Howard identification of propositions and types. This is what makes Agda a programming logic and not only a programming language. According to Curry-Howard, we identify programs and proofs, something which is possible only by requiring that all program terminate. However, at the end of these notes we present a method for encoding partial and general recursive functions as total functions using dependent types.

1

What are Dependent Types?

Dependent types are types that depend on elements of other types. An example is the type An of vectors of length n with components of type A. Another example is the type Am×n of m × n-matrices. We say that the type An depends on the number n, or that An is a family of types indexed by the number n. Yet another example is the type of trees of a certain height. With dependent types we can also define the type of height-balanced trees of a certain height, that is, trees where the height of subtrees differ by at most one. As we will see, more complicated invariants can also be expressed with dependent type. We can have a type of sorted lists and a type of sorted binary trees (binary search trees). In fact, we shall use the strong system of dependent types of the Agda language [3,26] which is an extension of Martin-L¨ of type theory [20,21,22,25]. In this language we can express more or less any conceivable property. We have to say “more or less” because G¨ odel’s incompleteness theorem sets a limit for the expressiveness of logical languages. Parametrised types, such as the type [A] of lists of elements of type A, are usually not called dependent types. These are families of types indexed by other types, not families of types indexed by elements of another type. However, in dependent type theories there is a type of small types (a universe), so that we have a type [A] of lists of elements of a given small type A.

Already FORTRAN allowed you to define arrays of a given dimension, and in this sense, dependent types are as old as high-level programming languages. However, the simply typed lambda calculus and the Hindley-Milner type system, on which typed functional programming languages such as ML [24] and Haskell [27] are based, do not include dependent types, only parametrised types. Similarly, the polymorphic lambda calculus System F [15] has types like ∀X.A where X ranges over all types, but no quantification over elements of other types. The type systems of typed functional programming languages have gradually been extended with new features. One example is the module system of SML; others are the arrays and the newly introduced generalised algebraic data types of Haskell [28]. Moreover, a number of experimental functional languages with limited forms of dependent types have been introduced recently. Examples include meta-ML (for meta-programming) [32], PolyP [18] and Generic Haskell [17] (for generic programming), and dependent ML [29] (for programming with “indexed” types). It turns out that many of these features can be modelled by the dependent types in the strong type system we consider in these notes. The modern development of dependently typed programming languages has its origins in the Curry-Howard isomorphism between propositions and types. Already in the 1930’s Curry noticed the similarity between the axioms of implicationa