Curry-Howard-Lambek correspondence

From HaskellWiki


Haskell theoretical foundations

General:
Mathematics - Category theory
Research - Curry/Howard/Lambek

Lambda calculus:
Alpha conversion - Beta reduction
Eta conversion - Lambda abstraction

Other:
Recursion - Combinatory logic
Chaitin's construction - Turing machine
Relational algebra

The Curry-Howard-Lambek correspondance is a three way isomorphism between types (in programming languages), propositions (in logic) and objects of a Cartesian closed category. Interestingly, the isomorphism maps programs (functions in Haskell) to (constructive) proofs in logic (and vice versa).

Life, the Universe and Everything[edit]

As is well established by now,

theAnswer :: Integer
theAnswer = 42

The logical interpretation of the program is that the type Integer is inhabited (by the value 42), so the existence of this program proves the proposition Integer (a type without any value is the "bottom" type, a proposition with no proof).

Inference[edit]

A (non-trivial) Haskell function maps a value (of type a, say) to another value (of type b), therefore, given a value of type a (a proof of a), it constructs a value of type b (so the proof is transformed into a proof of b)! So b is inhabited if a is, and a proof of a -> b is established (hence the notation, in case you were wondering).

representation :: Bool -> Integer
representation False = 0
representation True = 1

says, for example, if Boolean is inhabited, so is Integer (well, the point here is demonstration, not discovery).

Connectives[edit]

Of course, atomic propositions contribute little towards knowledge, and the Haskell type system incorporates the logical connectives Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \and} and Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \or} , though heavily disguised. Haskell handles Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \or} conjuction in the manner described by Intuitionistic Logic. When a program has type Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle A \or B} , the value returned itself indicates which one. The algebraic data types in Haskell has a tag on each alternative, the constructor, to indicate the injections:

data Message a = OK a | Warning a | Error a

p2pShare :: Integer -> Message String
p2pShare n | n == 0 = Warning "Share! Freeloading hurts your peers."
           | n < 0 = Error "You cannot possibly share a negative number of files!"
           | n > 0 = OK ("You are sharing " ++ show n ++ " files.")

So any one of OK String, Warning String or Error String proves the proposition Message String, leaving out any two constructors would not invalidate the program. At the same time, a proof of Message String can be pattern matched against the constructors to see which one it proves. On the other hand, to prove String is inhabited from the proposition Message String, it has to be proven that you can prove String from any of the alternatives...

show :: Message String -> String
show (OK s) = s
show (Warning s) = "Warning: " ++ s
show (Error s) = "ERROR! " ++ s

The Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \and} conjuction is handled via an isomorphism in Closed Cartesian Categories in general (Haskell types belong to this category): Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \mathrm{Hom}(X\times Y,Z) \cong \mathrm{Hom}(X,Z^Y)} . That is, instead of a function from Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle X \times Y} to Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle Z} , we can have a function that takes an argument of type Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle X} and returns another function of type Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle Y \rightarrow Z} , that is, a function that takes Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle Y} to give (finally) a result of type Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle Z} : this technique is (known as currying) logically means Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle A \and B \rightarrow C \iff A \rightarrow (B \rightarrow C)} .

(insert quasi-funny example here)

So in Haskell, currying takes care of the Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle \and} connective. Logically, a proof of Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle A \and B} is a pair Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle (a, b)} of proofs of the propositions. In Haskell, to have the final Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle C} value, values of both Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle A} and Failed to parse (SVG (MathML can be enabled via browser plugin): Invalid response ("Math extension cannot connect to Restbase.") from server "https://wikimedia.org/api/rest_v1/":): {\displaystyle B} have to be supplied (in turn) to the (curried) function.

Theorems for free![edit]

Things get interesting when polymorphism comes in. The composition operator in Haskell proves a very simple theorem.

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(.) f g x = f (g x)

The type is, actually, forall a b c. (b -> c) -> (a -> b) -> (a -> c), to be a bit verbose, which says, logically speaking, for all propositions a, b and c, if from a, b can be proven, and if from b, c can be proven, then from a, c can be proven (the program says how to go about proving: just compose the given proofs!)

Negation[edit]

Of course, there's not much you can do with just truth. forall b. a -> b says that given a, we can infer anything. Therefore we will take forall b. a -> b as meaning not a. Given this, we can prove several more of the axioms of logic.

type Not x = (forall a. x -> a)

doubleNegation :: x -> Not (Not x)
doubleNegation k pr = pr k

contraPositive :: (a -> b) -> (Not b -> Not a)
contraPositive fun denyb showa = denyb (fun showa)

deMorganI :: (Not a, Not b) -> Not (Either a b)
deMorganI (na, _) (Left a) = na a
deMorganI (_, nb) (Right b) = nb b

deMorganII :: Either (Not a) (Not b) -> Not (a,b)
deMorganII (Left na) (a, _) = na a
deMorganII (Right nb) (_, b) = nb b

Type classes[edit]

A type class in Haskell is a proposition about a type.

class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool

means, logically, there is a type a for which the type a -> a -> Bool is inhabited, or, from a it can be proved that a -> a -> Bool (the class promises two different proofs for this, having names == and /=). This proposition is of existential nature (not to be confused with existential type). A proof for this proposition (that there is a type that conforms to the specification) is (obviously) a set of proofs of the advertised proposition (an implementation), by an instance declaration:

instance Eq Bool where
    True  == True  = True
    False == False = True
    _     == _     = False

(/=) a b = not (a == b)

A not-so-efficient sort implementation would be:

sort [] = []
sort (x : xs) = sort lower ++ [x] ++ sort higher
                     where (lower,higher) = partition (< x) xs

Haskell infers its type to be forall a. (Ord a) => [a] -> [a]. It means, if a type a satisfies the proposition about propositions Ord (that is, has an ordering defined, as is necessary for comparison), then sort is a proof of [a] -> [a]. For this to work, somewhere, it should be proved (that is, the comparison functions defined) that Ord a is true.

Multi-parameter type classes[edit]

Haskell makes frequent use of multiparameter type classes. Type classes constitute a Prolog-like logic language, and multiparameter type classes define a relation between types.

Functional dependencies[edit]

These type level functions are set-theoretic. That is, class TypeClass a b | a -> b defines a relation between types a and b, and requires that there would not be different instances of TypeClass a b and TypeClass a c for different b and c, so that, essentially, b can be inferred as soon as a is known. This is precisely functions as relations as prescribed by set theory.

Indexed types[edit]

(please someone complete this, should be quite interesting, I have no idea what it should look like logically)