See the official user manual for the most up-to-date version of the information on this page.
What is Agda?
Agda is a dependently typed programming language. It is an extension of Martin-Löf's type theory, and is the latest in the tradition of languages developed in the programming logic group at Chalmers. Agda was mainly developed by Ulf Norell. Other languages in this tradition are Alf, Alfa, Agda-1, Cayenne. Some other loosely related languages are Coq, Epigram, Idris, ...
Because of strong typing and dependent types, Agda can be used as a proof assistant, allowing one to prove mathematical theorems (in a constructive setting) and to run such proofs as algorithms.
Dependent types
Typing for programmers
Type theory is concerned both with programming and logic. We see the type system as a way to express syntactic correctness. A type correct program has a meaning.
Lisp is a totally untyped programming language, and so are its derivatives like Scheme. In such languages, if f
is a function, one can apply it to anything, including itself. This makes it easy to write programs (almost all programs are wellformed), but it also makes it easy to write erroneous programs. Programs will raise exceptions or loop forever. And it is very difficult to analyse where the problems are.
Haskell or ML and its derivatives like SML and Caml are typed languages, where functions come with a type expressing what type of arguments the program expects and what the result type is.
Between these two families of languages come languages, which may or may not have a typing discipline. Most imperative languages do not come with a rich type system. For example,
C is typed, but very loosely (almost everything is an int
eger, or a variant thereof).
Moreover, the typing system does not allow the definition of trees or graphs without using pointers.
The functional and imperative languages are weakly typed, i.e. the result of computing the value of an expression e
of type T
is one of the following:
- the program terminates with a value in the type
T
- the program
e
does not terminate - the program raises an exception (which has been caused by an incomplete definition -- for instance a function is only defined for positive integers, but is applied to a negative integer.
Agda and type theory are strongly typed in the sense that a program e
of type T
always terminate with a value in T
. No runtime error can occur and no nonterminating programs can be written. (The latter statement needs some refinement.)
Dependent types
(see Dependently typed λ-calculus)
Dependent types are introduced by having families of types indexed by objects in another type. For instance, we can define the type Vec n
of vectors of length n
. This is a family of types indexed by objects in Nat
(a type parameterized by natural numbers).
Having dependent types, we must generalize the type of functions and the type of pairs.
The dependent function space (a : A) -> (B a)
is the type of functions taking an argument
a
in a type A
and a result in B a
. Here, A
is a type and B
is a family of types indexed by elements in A
.
For example, we could define the type of n x m
matrices as a type indexed by two natural numbers. Call this type Mat n m
. The identity function i
which
takes a natural number n
as argument and produces the n x n
identity matrix is then a function of type
i : (n : Nat) -> (Mat n n)
.
Remark: we could of course just specify the i
function with the type Nat -> Nat -> Mat
, where Mat
is the type of matrices; but this is not as precise as the dependent version.
The advantage of using dependent types is that it makes it possible to express properties of programs in the typing system. We saw above that it is possible to express the type of square matrices of length n
, it is also possible to define the type of operations on matrices so that the lengths are correct. For instance the type of matrix multiplication is
(i : Nat)(j : Nat)(k : Nat)(Mat i j) -> (Mat j k) -> (Mat i k)
and the type system can check that a program for matrix multiplication really takes arguments of the correct size. It can also check that matrix multiplication is only applied to matrices where the number of columns of the first argument is the same as the number of rows in the second argument.
Dependent types and logic
Thanks to the Curry-Howard isomorphism, one can express a logical specification using dependent types. Using only typing, it is for example possible to define
- equality on natural numbers
- properties of arithmetical operations
- the type
(n : Nat) -> (PrimRoot n)
consisting of functions computing primitive root in modular arithmetic.
Of course a program in the above type will be more difficult to write than the corresponding program of type Nat -> Nat
which produces a natural number which is a primitive root. However, the difficulty can be compensated by the fact that the program is guaranteed to work: it cannot produce something which is not a primitive root.
On a more mathematical level, we can express formulas and prove them using an algoritm. For example, a function of type
(n : Nat) -> (PrimRoot n)
is also a proof that every natural number has a primitive root.