Why dependent types

Posted on January 16, 2016
Tags: types

in accordance with the propositions-as-types paradigm. Thus, filling in the details of a function definition and ensuring it is type correct is no different from filling in the details of a proof and checking that it establishes the desired conclusion.

This post discuss why I care about dependent-types.

module whydp where

Types give us a notion of possible values. Polymorphic types are types that are indexed by other types; they are functions from types to types. Dependent types are types that are indexed by values; they are functions from values to types. Since types themselves are values of a higher type, the distinction between types and values is erased in dependently-typed theories: they are both values of specific types! –this is very different from many conventional programming languages. Alternatively, one may say ‘types are first-class citizens’ in such a language and so can be manipulated like any other value.

Observe that dependent types encompass the other two notions. Since simple types are just polymorphic types that ignore the index-type, we regain simple types from polymorphic types. Since types are the values of the type of ‘small types’, we regain polymorphic types from dependent types.

A dependent (function) type is usually written π x ∶ A • B x or as (x : A) → B x. Agda gives us the latter, but we can mimic the former via syntax declarations:

π∶• : (A : Set) (B : A  Set)  Set
π∶• A B = (x : A)  B x

syntax π∶• A  x  B) = π x ∶ A • B -- `∶` is a ghost colon obtained by `slash :` in emacs

Why the bullet-notation? We cannot use a low dot . since it is one of the few reserved tokens in Agda; more importantly, I like Z-notation style quantification. Anyhow, this is why the main page of this blog contains a Π; the Σ is the notation for dependent product: Σ x ∶ A • B x has values being pairs denoted (a , b) with a : A and b : B a; note the dependence of the second component’s type on that of the first component. The dependent product type Σ x ∶ A • B x can be read as ‘the type of x : A satisfying B’; a sort of ‘sub-type’ of A.

A final notation for dependent function is ∀ (x : A) → B x. The reason for this notation is that a universal-quantification can be proved true by constructing a procedure that for any element x : A produces a proof of B x, i.e., an inhabitant of the type B x. This is the Curry-Howard Correspondence: it can be stated as saying propositions coincide with types, or that Gentzen’s natural deduction is identical to Church’s lambda calculus. Loosely put, logic and computation are flip-sides of the same coin. For more on this, see Proofs are programs: 19th century logic and 21st century computing

Basically, we can express a type which is inhabited precisely when the associated proposition is true: for a proposition p : X → 𝔹, we can construct a dependent type P : (X : x) → Set of ‘’proofs witnessing p(x), given x’’; we can construct a value of type P x precisely when p(x) holds. If p(x) does not hold, then we cannot construct a value of type P x and there is no run-time exception but a compile-time check.

Essentially, a type Q can be thought of as “the type of reasons, or proofs, that Q is true”.

Programming

Usually, a program that takes input satisfying a given Precondition and ensuring a given Postcondition is usually programmed, call it f, then is proven correct according to the aforementioned specification by ‘verifying’ ∀ x • x ∈ Pre ⇒ f x ∈ Post and if that is too difficult, tests are formed after-which f is written and checked to satisfy the given tests; so-called test-driven development. However, since types correspond to propositions, it suffices to ‘construct a correct program from its specification’ as f : Pre → Post; so-called type-driven programming. This is not a new idea; consider abs : ℤ → ℤ with a proof ∀ n : ℤ • abs b ≥ 0 versus the single program abs : ℤ → ℕ. However, dependent types allow us to put more complicated specification details into the types.

Similar examples include,

The program-then-proof approach becomes simultaneously-program-and-prove, or test-driven becomes type-driven.

Dependent types can be used as specifications for programs that are a part of the programs themselves and not independently elsewhere, removed from the code. Then specification satisfiability is tantamount to type-checking!

Proving

I personally use the dependently-typed programming language Agda as a type checker for doing mathematics —manipulating symbols according to specified rules. Agda is used as just a mechanised mathematical notation that lets me write the mathematics in a natural way, and allows me to be confident about my proofs.

Theoretical developments can be fully mechanised and still be presented in readable calculational style, where writing is not significantly more effort than a conventional calculational presentation in LaTeX.Wolfram Kahl

Moreover he goes on to say,

Finally, proofs in Agda are fool-proof! Mechanised approaches do not allow us to leave anything as ‘an exercise to the reader’ –to the extent that we always have type-checking— and check every step of our proof. Sometime we may apply a lemma is a proof-step incorrectly on paper-and-pencil proofs and it might not at all be that obvious that we failed to meet some proviso, whereas in a mechanised setting, we can rest assured that such did not occur.

That is, the extra work of using a real language rather than pseudo-code is the type-checking: we can make sure all the i’s are dotted and t’s crossed.

A more personal reason is that mathematics is a hobby for me and there’s a proverb ‘I hear and forget, I see and remember, I do and understand’, so formalising the math I do is to ensure my comprehension —also it’s addictive fun :-)

Further Reading

Some pleasant introductions to dependent types, in Agda.