\subsection{Universes} The second programming technique we will look at that is not available in non-dependently typed languages is {\em universe construction}. First the module header. \begin{code} module Universes where \end{code} A universe is a set of types (or type formers) and a universe construction consists of a type of codes and a decoding function mapping codes to types in the universe. The purpose of a universe construction is to be able to define functions over the types of the universe by inspecting their codes. In fact we have seen an example of a universe construction already. \subsubsection{A familiar universe} The universe of decidable propositions consists of the singleton type \verb!True! and the empty type \verb!False!. Codes are booleans and the decoder is the \verb!isTrue! function. \begin{code} data False : Set where record True : Set where data Bool : Set where true : Bool false : Bool isTrue : Bool -> Set isTrue true = True isTrue false = False \end{code} Now functions over decidable propositions can be defined by manipulating the boolean codes. For instance, we can define negation and conjunction as functions on codes and prove some properties of the corresponding propositions. \begin{code} infix 30 not_ infixr 25 _and_ not_ : Bool -> Bool not true = false not false = true _and_ : Bool -> Bool -> Bool true and x = x false and _ = false notNotId : (a : Bool) -> isTrue (not not a) -> isTrue a notNotId true p = p notNotId false () andIntro : (a b : Bool) -> isTrue a -> isTrue b -> isTrue (a and b) andIntro true _ _ p = p andIntro false _ () _ \end{code} A nice property of this universe is that proofs of \verb!True! can be found automatically. This means that if you have a function taking a proof of a precondition as an argument, where you expect the precondition to be trivially true at the point where you are calling the function, you can make the precondition an implicit argument. For instance, if you expect to mostly divide by concrete numbers, division of natural numbers can be given the type signature \begin{code} open import Data.Nat nonZero : Nat -> Bool nonZero zero = false nonZero (suc _) = true postulate _div_ : Nat -> (m : Nat){p : isTrue (nonZero m)} -> Nat three = 16 div 5 \end{code} Here the proof obligation \verb!isTrue (nonZero 5)! will reduce to \verb!True! and solved automatically by the type checker. Note that if you tell the type checker that you have defined the type of natural numbers, you are allowed to use natural number literals like \verb!16! and \verb!5!. This has been done in the library. \subsubsection{Universes for generic programming} Generic programming deals with the problem of defining functions generically over a set of types. We can achieve this by defining a universe for the set of types we are interested in. Here is a simple example of how to program generically over the set of types computed by fixed points over polynomial functors. First we define a type of codes for polynomial functors. \begin{code} data Functor : Set1 where |Id| : Functor |K| : Set -> Functor _|+|_ : Functor -> Functor -> Functor _|x|_ : Functor -> Functor -> Functor \end{code} A polynomial functor is either the identity functor, a constant functor, the disjoint union of two functors, or the cartesian product of two functors. Since codes for functors can contain arbitrary \verb!Set!s (in the case of the constant functor) the type of codes cannot itself be a \verb!Set!, but lives in \verb!Set1!. Before defining the decoding function for functors we define datatypes for disjoint union and cartesian product. \begin{code} data _⊕_ (A B : Set) : Set where inl : A -> A ⊕ B inr : B -> A ⊕ B data _×_ (A B : Set) : Set where _,_ : A -> B -> A × B infixr 50 _|+|_ _⊕_ infixr 60 _|x|_ _×_ \end{code} The decoding function takes a code for a functor to a function on \verb!Set!s and is computed recursively over the code. \begin{code} [_] : Functor -> Set -> Set [ |Id| ] X = X [ |K| A ] X = A [ F |+| G ] X = [ F ] X ⊕ [ G ] X [ F |x| G ] X = [ F ] X × [ G ] X \end{code} Since it's called a functor it ought to support a map operation. We can define this by recursion over the code. \begin{code} map : (F : Functor){X Y : Set} -> (X -> Y) -> [ F ] X -> [ F ] Y map |Id| f x = f x map (|K| A) f c = c map (F |+| G) f (inl x) = inl (map F f x) map (F |+| G) f (inr y) = inr (map G f y) map (F |x| G) f (x , y) = map F f x , map G f y \end{code} Next we define the least fixed point of a polynomial functor. \begin{code} data μ_ (F : Functor) : Set where <_> : [ F ] (μ F) -> μ F \end{code} To ensure termination, recursive datatypes must be strictly positive and this is checked by the type checker. Our definition of least fixed point goes through, since the type checker can spot that \verb![_]! is strictly positive in its second argument. With this definition we can define a generic fold operation on least fixed points. Grabbing for the closest category theory text book we might try something like this \begin{code} -- fold : (F : Functor){A : Set} -> ([ F ] A -> A) -> μ F -> A -- fold F φ < x > = φ (map F (fold F φ) x) \end{code} Unfortunately, this definition does not pass the termination checker since the recursive call to \verb!fold! is passed to the higher order function \verb!map! and the termination checker cannot see that \verb!map! isn't applying it to bad things. To make \verb!fold! pass the termination checker we can fuse \verb!map! and \verb!fold! into a single function \verb!mapFold F G φ x = map F (fold G φ) x! defined recursively over \verb!x!. We need to keep two copies of the functor since \verb!fold! is always called on the same functor, whereas \verb!map! is defined by taking its functor argument apart. \begin{code} mapFold : forall {X} F G -> ([ G ] X -> X) -> [ F ] (μ G) -> [ F ] X mapFold |Id| G φ < x > = φ (mapFold G G φ x) mapFold (|K| A) G φ c = c mapFold (F₁ |+| F₂) G φ (inl x) = inl (mapFold F₁ G φ x) mapFold (F₁ |+| F₂) G φ (inr y) = inr (mapFold F₂ G φ y) mapFold (F₁ |x| F₂) G φ (x , y) = mapFold F₁ G φ x , mapFold F₂ G φ y fold : {F : Functor}{A : Set} -> ([ F ] A -> A) -> μ F -> A fold {F} φ < x > = φ (mapFold F F φ x) \end{code} There is a lot more fun to be had here, but let us make do with a couple of examples. Both natural numbers and lists are examples of least fixed points of polynomial functors: \begin{code} NatF = |K| True |+| |Id| NAT = μ NatF Z : NAT Z = < inl _ > S : NAT -> NAT S n = < inr n > ListF = \A -> |K| True |+| |K| A |x| |Id| LIST = \A -> μ (ListF A) nil : {A : Set} -> LIST A nil = < inl _ > cons : {A : Set} -> A -> LIST A -> LIST A cons x xs = < inr (x , xs) > \end{code} To make implementing the argument to fold easier we introduce a few helper functions: % \begin{code} [_||_] : {A B C : Set} -> (A -> C) -> (B -> C) -> A ⊕ B -> C [ f || g ] (inl x) = f x [ f || g ] (inr y) = g y uncurry : {A B C : Set} -> (A -> B -> C) -> A × B -> C uncurry f (x , y) = f x y const : {A B : Set} -> A -> B -> A const x y = x \end{code} % Finally some familiar functions expressed as folds. % \begin{code} foldr : {A B : Set} -> (A -> B -> B) -> B -> LIST A -> B foldr {A}{B} f z = fold [ const z || uncurry f ] plus : NAT -> NAT -> NAT plus n m = fold [ const m || S ] n \end{code} \subsubsection{Universes for overloading} At the moment, Agda does not have a class system like the one in Haskell. However, a limited form of overloading can be achieved using universes. The idea is simply if you know in advance at which types you want to overload a function, you can construct a universe for these types and define the overloaded function by pattern matching on a code. A simple example: suppose we want to overload equality for some of our standard types. We start by defining our universe: \begin{code} open import Data.List data Type : Set where bool : Type nat : Type list : Type -> Type pair : Type -> Type -> Type El : Type -> Set El nat = Nat El bool = Bool El (list a) = List (El a) El (pair a b) = El a × El b \end{code} In order to achieve proper overloading it is important that we don't have to supply the code explicitly everytime we are calling the overloaded function. In this case we won't have to since the decoding function computes distinct datatypes in each clause. This means that the type checker can figure out a code from its decoding. For instance, the only code that can decode into \verb!Bool! is \verb!bool!, and if the decoding of a code is a product type then the code must be \verb!pair! of some codes. Now an overloaded equality function simply takes an implicit code and computes a boolean relation over the semantics of the code. \begin{code} infix 30 _==_ _==_ : {a : Type} -> El a -> El a -> Bool _==_ {nat} zero zero = true _==_ {nat} (suc _) zero = false _==_ {nat} zero (suc _) = false _==_ {nat} (suc n) (suc m) = n == m _==_ {bool} true x = x _==_ {bool} false x = not x _==_ {list a} [] [] = true _==_ {list a} (_ :: _) [] = false _==_ {list a} [] (_ :: _) = false _==_ {list a} (x :: xs) (y :: ys) = x == y and xs == ys _==_ {pair a b} (x₁ , y₁) (x₂ , y₂) = x₁ == x₂ and y₁ == y₂ \end{code} In the recursive calls of \verb!_==_! the code argument is inferred automatically. The same happens when we use our equality function on concrete examples: \begin{code} example₁ : isTrue (2 + 2 == 4) example₁ = _ example₂ : isTrue (not (true :: false :: [] == true :: true :: [])) example₂ = _ \end{code} In summary, universe constructions allows us to define functions by pattern matching on (codes for) types. We have seen a few simple examples, but there are a lot of other interesting possibilities. For example \begin{itemize} \item XML schemas as codes for the types of well-formed XML documents, \item a universe of tables in a relational database, allowing us to make queries which are guaranteed to be well-typed \end{itemize}