User:Michiexile/MATH198/Lecture 5

From HaskellWiki

Cartesian Closed Categories and typed lambda-calculus

A category is said to have pairwise products if for any objects A,B, there is a product object A×B.

A category is said to have pairwise coproducts if for any objects A,B, there is a coproduct object A+B.

Recall when we talked about internal homs in Lecture 2. We can now define what we mean, formally, by the concept:

Definition An object C in a category D is an internal hom object or an exponential object [AB] or BA if it comes equipped with an arrow ev:[AB]×AB, called the evaluation arrow, such that for any other arrow f:C×AB, there is a unique arrow λf:C[AB] such that the composite

C×Aλf×1A[AB]×AevB

is f.

The idea here is that with something in an exponential object, and something in the source of the arrows we imagine live inside the exponential, we can produce the evaluation of the arrow at the source to produce something in the target. Using global elements, this reasoning comes through in a more natural manner: given f:1[AB] and x:1A we can produce the global element f(x)=evf×x:1B. Furthermore, we can always produce something in the exponential whenever we have something that looks as if it should be there.

And with this we can define

Definition A category C is a Cartesian Closed Category or a CCC if:

  1. C has a terminal object 1
  2. Each pair of objects A,BC0 has a product A×B and projections p1:A×BA, p2:A×BB.
  3. For every pair A,BC0 of objects, there is an exponential object [AB] with an evaluation map [AB]×AB.

Currying

Note that the exponential as described here is exactly what we need in order to discuss the Haskell concept of multi-parameter functions. If we consider the type of a binary function in Haskell:

binFunction :: a -> a -> a

This function really lives in the Haskell type a -> (a -> a), and thus is an element in the repeated exponential object [A[AA]]. Evaluating once gives us a single-parameter function, the first parameter consumed by the first evaluation, and we can evaluate a second time, feeding in the second parameter to get an end result from the function.

On the other hand, we can feed in both values at once, and get

binFunction' :: (a,a) -> a

which lives in the exponential object [A×AA].

These are genuinely different objects, but they seem to do the same thing: consume two distinct values to produce a third value. The resolution of the difference lies, again, in a recognition from Set theory: there is an isomorphism

Hom(S,Hom(T,V))=Hom(S×T,V)

which we can use as inspiration for an isomorphism Hom(S,[TV])=Hom(S×T,V) valid in Cartesian Closed Categories.


Typed lambda-calculus

The lambda-calculus, and later the typed lambda-calculus both act as foundational bases for computer science, and computer programming in particular. The idea in both is that everything is a function, and we can reduce the act of programming to function application; which in turn can be analyzed using expression rewriting rules that encapsulate the act of computation in a sequence of formal rewrites.

Definition A typed lambda-calculus is a formal theory with types, terms, variables and equations. Each term a has a type A associated to it, and we write a:A or aA. The system is subject to a sequence of rules:

  1. There is a type 1. Hence, the empty lambda calculus is excluded.
  2. If A,B are types, then so are A×B and [AB]. These are, initially, just additional symbols, not imbued with the associations we usually give the symbols used.
  3. There is a term *:1. Hence, the lambda calculus without any terms is excluded.
  4. For each type A, there is an infinite (countable) supply of terms xAi:A.
  5. If a:A,b:B are terms, then there is a term (a,b):A×B.
  6. If c:A×B then there are terms proj1(c):A,proj2(c):B.
  7. If a:A And f:[AB], then there is a term fa:B.
  8. If x:A is a variable and ϕ(x):B is a term, then there is a λxAϕ(x):[AB]. Note that here, ϕ(x) is a meta-expression, meaning we have SOME lambda-calculus expression that may include the variable x.
  9. There is a relation a=Xa for each set of variables X that occur freely in either a or a. This relation is reflexive, symmetric and transitive. Recall that a variable is free in a term if it is not in the scope of a λ-expression naming that variable.
  10. If a:1 then a={}*. In other words, up to lambda-calculus equality, there is only one value of type *.
  11. If XY, then a=Xa implies a=Ya. Binding more variables gives less freedom, not more, and thus cannot suddenly make equal expressions differ.
  12. a=Xa implies fa=Xfa.
  13. f=Xf implies fa=Xfa. So equality plays nice with function application.
  14. ϕ(x)=X{x}ϕ(x) implies λxϕ(x)=Xλxϕ(x). Equality behaves well with respect to binding variables.
  15. proj1(a,b)=Xa, proj2(a,b)=Xb, c=X(proj1(c),proj2(c)) for all a,b,c,X.
  16. λxϕ(x)a=Xϕ(a) if a is substitutable for x in ϕ(x) and ϕ(a) is what we get by substituting each occurrence of x bya in ϕ(x). A term is substitutable for another if by performing the substitution, no occurrence of any variable in the term becomes bound,
  17. λxAfx=Xf, provided x∉X.
  18. λxAϕ(x)=XλxAϕ(x) if x is substitutable for x in ϕ(x) and each variable is not free in the other expression.

Note that =X is just a symbol. The axioms above give it properties that work a lot like equality, but two lambda calculus-equal terms are not equal unless they are identical. However, a=Xb tells us that in any model of this lambda calculus - where terms, types, et.c. are replaced with actual things (mathematical objects, say, or a programming language semantics embedding typed lambda calculus) - then the things given by translating a and b into the model should end up being equal.

Any actual realization of typed lambda calculus is bound to have more rules and equalities than the ones listed here.

With these axioms in front of us, however, we can see how lambda calculus and Cartesian Closed Categories fit together: We can go back and forth between the wo concepts in a natural manner:

Lambda to CCC

Given a typed lambda calculus L, we can define a CCC C(L). Its objects are the types of L. An arrow from A to B is an equivalence class (under ={x}) of terms of type B, free in a single variable x:A.

We need the equivalence classes because for any variable x:A, we want λxx:1[AA] to be the global element of [AA] corresponding to the identity arrow. Hence, that variable must itself correspond to an identity arrow.

And then the rules for the various constructions enumerated in the axioms correspond closely to what we need to prove the resulting category to be cartesian closed.

CCC to Lambda

To go in the other direction, starting out with a Cartesian Closed Category and finding a typed lambda calculus corresponding to it, we construct its internal language.

Given a CCC C, we can assume that we have chosen, somehow, one actual product for each finite set of factors. Thus, both all products and all projections are well defined entities, with no remaining choice to determine them.

The types of the internal language L(C) are just the objects of C. The existence of products, exponentials and terminal object covers axioms 1-2. We can assume the existence of variables for each type, and the remaining axioms correspond to definition and behaviour of the terms available.

Using the properties of a CCC, it is at this point possible to prove a resulting equivalence of categories C(L(C))=C, and similarly, with suitable definitions for what it means for formal languages to be equivalent, one can also prove for a typed lambda-calculus L that L(C(L))=L.


More on this subject can be found in:

  • Lambek & Scott: Aspects of higher order categorical logic and Introduction to higher order categorical logic

More importantly, by stating λ-calculus in terms of a CCC instead of in terms of terms and rewriting rules is that you can escape worrying about variable clashes, alpha reductions and composability - the categorical translation ignores, at least superficially, the variables, reduces terms with morphisms that have equality built in, and provides associative composition for free.

At this point, I'd recommend reading more on Wikipedia [1] and [2], as well as in Lambek & Scott: Introduction to Higher Order Categorical Logic. The book by Lambek & Scott goes into great depth on these issues, but may be less than friendly to a novice.


Limits and colimits

One design pattern, as it were, that we have seen occur over and over in the definitions we've seen so far is for there to be some object, such that for every other object around, certain morphisms have unique existence.

We saw it in terminal and initial objects, where there's a unique map from or to every other object. And in products/coproducts where a wellbehaved map, capturing any pair of maps has unique existence. And finally, above, in the CCC characterization of the internal hom, we had a similar uniqueness requirement for the lambda map.

One thing we can notice is that the isomorphisms theorems for all these cases look very similar to each other: in each isomorphism proof, we produce the uniquely existing morphisms, and prove that their uniqueness and their other properties force the maps to really be isomorphisms.

Now, category theory has a philosophy slightly similar to design patterns - if we see something happening over and over, we'll want to generalize it. And there are generalizations available for these!

Diagrams, cones and limits

Definition A diagram D of the shape of an index category J (often finite or countable), in a category C is just a functor D:JC. Objects in J will be denoted by i,j,k,... and their images in C by Di,Dj,Dk,....

This underlines that when we talk about diagrams, we tend to think of them less as just functors, and more as their images - the important part of a diagram D is the objects and their layout in C, and not the process of going to C from D.

Definition A cone over a diagram D in a category C is some object C equipped with a family ci:CDi of arrows, one for each object in J, such that for each arrow α:ij in J, the following diagram

commutes, or in equations, Dαci=cj.

A morphism f:(C,ci)(C,c'i) of cones is an arrow f:CC such that each triangle

commutes, or in equations, such that cj=c'jf.

This defins a category of cones, that we shall denote by Cone(D). And we define, hereby:

Definition The limit of a diagram D in a category C is a terminal object in Cone(D). We often denote a limit by

pi:limjDjDi

so that the map from the limit object limjDj to one of the diagram objects Di is denoted by pi.

The limit being terminal in the category of cones nails down once and for all the uniqueness of any map into it, and the isomorphism of any two terminal objects carries over to a proof once and for all for the limit case.

Specifically, since the morphisms of cones are morphisms in C, and composition is carried straight over, so proving a map is an isomorphism in the cone category implies it is one in the target category as well.

Definition A category C has all (finite) limits if all diagrams (of finite shape) have limit objects defined for them.

Limits we've already seen

The terminal object of a category is the limit object of an empty diagram. Indeed, it is an object, with no specified maps to no other objects, such that every other object that also maps to the same empty set of objects - which is to say all other objects - have a uniquely determined map to the limit object.

The product of some set of objects it she limit object of the diagram containing all these objects and no arrows; a diagram of the shape of a discrete category. The condition here becomes the requirement of maps to all factors so any other cone factors through these maps.

To express the exponential as a limit, we need to go to a different category than the one we started in. Take the category with objects given by morphisms X×YZ for fixed objects Y,Z, and morphisms given by morphisms X×YX×Y commuting with the 'objects' they run between and fixing Y. The exponential is a terminal object in this category.

Adding further arrows to diagrams amounts to adding further conditions on the products, as the maps from the product to the diagram objects need to factor through any arrows present in the diagram.

These added relations, however, is exactly what trips things up in Haskell. The idealized Haskell category does not have even all finite limits. At the core of the issue here is the lack of dependent types: there is no way for the type system to guarantee equations, and hence only the trivial limits - the products - can be guaranteed by the Haskell type checked.

In order to get that kind of guarantees, the type checker would need an implementation of Dependent type, something that can be simulated in several ways, but is not (yet) an actual part of Haskell. Other languages, however, cover this - most notably Epigram, Agda and Cayenne - which the latter is much stronger influenced by constructve type theory and category theory even than Haskell.

The kind of equations that show up in a limit, however, could be thought of as invariants for the type - and thus something that can be tested for. The resulting equations can be plugged into a testing framework - such as QuickCheck to verify that the invariants hold under the functions applied.

Colimits

The dual concept to a limit is defined using the dual to the cones:

Definition A cocone over a diagram D:JC is an object C with arrows cj:DjC such that for each arrow α:ij in J, the following diagram

commutes, or in equations, such that cjDα=ci.

A morphism f:(C,ci)(C,c'i) of cocones is an arrow f:CC such that each triangle

commutes, or in equations, such that cj=c'jf.

Just as with the category of cones, this yields a category of cocones, that we denote by Cocone(D), and with this we define:

Definition The colimit of a diagram D:JC is an initial object in Cocone(D).

We denote the colimit by

ii:DilimjDj

so that the map from one of the diagram objects Di to the colimit object limjDj is denoted by ii.

Again, the isomorphism results for coproducts and initial objects follow from that for the colimit, and the same proof ends up working for all colimits.

And again, we say that a category has (finite) colimits if every (finite) diagram admits a colimit.

Colimits we've already seen

The initial object is the colimit of the empty diagram.

The coproduct is the colimit of the discrete diagram.

For both of these, the argument is almost identical to the one in the limits section above.


Homework

Credit will be given for up to 4 of the 6 exercises.

  1. Prove that currying/uncurrying are isomorphisms in a CCC. Hint: the map fλf is a map Hom(C×A,B)Hom(C,[AB]).
  2. Prove that in a CCC λev is λev=1[AB]:[AB][AB].
  3. What is the limit of a diagram of the shape of the category 2?
  4. Is the category of Sets a CCC? Prove it.
  5. Is the category of vector spaces a CCC? Prove it.
  6. * Implement a typed lambda calculus as an EDSL in Haskell.