Idris: a Functional Programming Language with Dependent Types
Total Page:16
File Type:pdf, Size:1020Kb
Programming Languages and Compiler Construction Department of Computer Science Christian-Albrechts-University of Kiel Seminar Paper Idris: A Functional Programming Language with Dependent Types Author: B.Sc. Finn Teegen Date: 20th February 2015 Advised by: M.Sc. Sandra Dylus Contents 1 Introduction1 2 Fundamentals2 2.1 Universes....................................2 2.2 Type Families..................................2 2.3 Dependent Types................................3 2.4 Curry-Howard Correspondence........................4 3 Language Overview5 3.1 Simple Types and Functions..........................5 3.2 Dependent Types and Functions.......................6 3.3 Implicit Arguments...............................7 3.4 Views......................................8 3.5 Lazy Evaluation................................8 3.6 Syntax Extensions...............................9 4 Theorem Proving 10 4.1 Propositions as Types and Terms as Proofs................. 10 4.2 Encoding Intuitionistic First-Order Logic................... 12 4.3 Totality Checking................................ 14 5 Conclusion 15 ii 1 Introduction In conventional Hindley-Milner based programming languages, such as Haskell1, there is typically a clear separation between values and types. In dependently typed languages, however, this distinction is less clear or rather non-existent. In fact, types can depend on arbitrary values. Thus, they become first-class citizens and are computable like any other value. With types being allowed to contain values, they gain the possibility to describe prop- erties of their own elements. The standard example for dependent types is the type of lists of a given length - commonly referred to as vectors - where the length is part of the type itself. When starting to encode properties of values as types, the elements of such types can be seen as proofs that the stated property is true. This encoding allows the use of programming languages with dependent types as a logic. In order for this logic to be consistent, programs have to be required to be total, meaning that they are not allowed to crash or to be non-terminating. By using dependent types to encode not only properties of values but also properties of functions, we get the ability to make precise statements about the correctness of programs, giving us a higher reliability. This paper gives a brief introduction to Idris2, a general-purpose functional program- ming language with dependent types. It is heavily influenced by Haskell and Agda3 and has support for tactic based theorem proving similar to Coq4. Idris is mainly designed for verifiable systems programming and, to this end, it is a compiled language.[1,4,2] The further chapters are structured as follows. At first, some basic background knowl- edge is provided. This includes, inter alia, a formal definition of dependent types and a short introduction to the Curry-Howard correspondence that formulates the relationship between programs and proofs. In Chapter 3, we describe and exemplify the basic features of Idris as well as a few advanced ones. In addition, some programming techniques are demonstrated which are made available by those features. Afterwards, we demonstrate how Idris can be used for proving theorems in Chapter 4. We further show an encoding of intuitionistic first-order logic in Idris. At the very end, we discuss the pros and cons that may result from the use of Idris (and dependently typed languages in general) and relate Idris to other dependently typed languages. 1http://www.haskell.org 2http://www.idris-lang.org/ 3http://wiki.portal.chalmers.se/agda/pmwiki.php 4https://coq.inria.fr/ 1 2 Fundamentals This chapter gives an introduction to the theoretical foundations of dependent types, which include universes and type families. Furthermore, the Curry-Howard correspon- dence is shortly explained. 2.1 Universes With types being first-class citizens, they necessarily have types themselves. One could naively wish for a type U that contains all types (including itself). Such types whose elements are types are called universes. By definition the universe U itself then would also have the type U for which we write U : U. However, U : U leads to a paradox, known as Russell’s paradox.[8] Let R be the set of all sets which are not members of themselves, so R = fx j x2 = xg. If R contains itself, then it contradicts its own definition as the set of all sets that are not members of themselves. On the other hand, if R is not a member of itself, then its definition dictates that it has to contain itself. Symbolically: R 2 R , R2 = R An elegant solution to Russell’s paradox is the introduction of hierarchical universes. In this hierarchy every universe Ui is a member of the next universe Ui+1. Moreover, these universes are cumulative, i.e., if A : Ui then also A : Ui+1: U0 : U1 : U2 : ::: By saying that A has a type, we actually mean that A : Ui for some i. However, usually we just write A : U and omit the level i. This way, we even can write U : U which basically means Ui : Ui+1 for some i. 2.2 Type Families A collection of types B indexed by a given type A is called a family of types or indexed type family and is modeled by a function B : A !U whose codomain is a universe. Such collections are also denoted as dependent types.[8] An example of a family of types is the family VectR : N !U where VectR(n) is a real vector of length n. Parameterized data types are another example of indexed type families. For instance, List : U!U is the family of lists where List(A) is the type of lists that contains only elements of type A. 2 2 Fundamentals 2.3 Dependent Types Like in this paper, when speaking of dependent types, often an indexed type family is meant (see Section 2.2), although that term would also include the dependent product type and the dependent sum type. Dependent Product Type The dependent product type - also called dependent function type or pi-type - is the type of functions that, given a type A : U in a universe of types U and a family of types B : A !U, assign to each element x : A an element of the type B(x): U. In other words, the inhabitants of a dependent product type are functions whose codomain type may vary depending on their argument. The type of such dependent functions would be noted as follows: Π(x:A) B(x) For example, let VectR(n) again be the type of real vectors of length n. Π(n:N) VectR(n) would then be the type of functions that return a real vector of length n for a given natural number n. Note, the type of polymorphic functions is a dependent function type, too. The type of a polymorphic function returning elements of type C would be Π(A:U)A ! C. If B is a constant, the dependent product type simply becomes the ordinary function type A ! B. Dependent Sum Type Given again a type A : U and a family of types B : A !U, the dependent sum type, dependent pair type or sigma-type is the type of pairs that, having an element x : A as its first component, have an element of the type B(x): U as its second component. Thus, the type of a pair’s second value may depend on its first value. The depending sum type is notationally written in the following way: Σ(x:A)B(x) Once more, we use real vectors as example. If (l; v) is a dependent pair of type Σ(n:N) VectR(n), then l has the type N and v has the type VectR(l). If B is a constant, then the dependent pair type is judgmentally equal to the Cartesian product type A × B. 3 2 Fundamentals 2.4 Curry-Howard Correspondence The Curry-Howard correspondence is the observation that there is an isomorphism be- tween computational calculi as found in type theory and systems of formal logic as encountered in proof theory.[8,7] The dependent type theory, for instance, corresponds to intuitionistic first-order logic. In general, the correspondence says that types correlate with propositions, terms with proofs and type inhabitation with provability. In particular, the unit type behaves the same as the true proposition, the empty type as the false proposition, a function type as implication, a product type as conjunction, and a sum type as disjunction. The depen- dent product type and dependent sum type are isomorphic to universal quantification and existential quantification, respectively.[8] Table 2.1 summarizes the correspondence between types and propositions: Type theory Logic 1 True 0 False P ! QP ) Q P × QP ^ Q P + QP _ Q Π(x:X) P(x) 8x 2 X : P(x) Σ(x:X) P(x) 9x 2 X : P(x) Table 2.1: Analogy between type theory and logic 4 3 Language Overview This chapter covers Idris in its role as a general-purpose language. In this and the following chapters, it is assumed that the reader is familiar with Haskell. Therefore, we focus in this chapter mainly on the differences between Idris and Haskell and on the introduction of features that are specific to Idris. 3.1 Simple Types and Functions In this section, we describe how simple data types and function are defined and used in Idris. Primitive Types Just like Haskell, Idris has built-in a number of primitive types: Int for integers, Float for floating point numbers, Char for characters and String for character sequences. A noticeable difference to Haskell is that String in Idris is not a list of Char, but a primitive data type. Data Types Data types are declared in a similar way to Haskell data types, with a similar syntax.