sum
and psum
mRTheorem
mRTheorem
on plusIn a previous lecture we saw tha programs are proofs and we proved a higher order property, i.e., a property over functions, that all increasing functions are monotonic.
In this lecture, we see that any higher order property can be encoded in Liquid Haskell, because refinement types can encode higher order logic and type checking can encode the natural deduction proof system.
Like a programming language, logic has
Natural Deduction is a decision procedure for logic. The Curry-Howard correspondence says that propositions are types and proofs are programs. In this lecture, we will see that natural deduction is encoded by type checking rules.
Concretely, we will see the encoding by filling in the following table:
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | |
Conjunction | \(\phi_1 \land \phi_2\) | |
Disjunction | \(\phi_1 \lor \phi_2\) | |
Implication | \(\phi_1 \Rightarrow \phi_2\) | |
Negation | \(\lnot \phi\) | |
Forall | \(\forall x. \phi\) | |
Exists | \(\exists x. \phi\) |
We start with the simplest case, native terms. We have already seen that native terms are encoded by refinements on unit types:
So native terms are encoded by unit types with refinements:
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | \(\{v:() | e\}\) |
Conjunction | \(\phi_1 \land \phi_2\) | |
Disjunction | \(\phi_1 \lor \phi_2\) | |
Implication | \(\phi_1 \Rightarrow \phi_2\) | |
Negation | \(\lnot \phi\) | |
Forall | \(\forall x. \phi\) | |
Exists | \(\exists x. \phi\) |
Conjunction is encoded by the pair type. If you know that \(\phi_1\) is true and \(\phi_2\) is true, then \((\phi_1, \phi_2)\) is a term that shows that \(\phi_1 \land \phi_2\) is true.
Deduction Rules: The rules of natural deduction are the rules that we can use to prove a formula. For conjuctions, there are three rules:
These rules are part of the natural deduction proof system and can also be encoded as refinement type checking rules.
Rule | Natural Deduction | Type Checking |
---|---|---|
Left Elimination | \(\inferrule{} { \Gamma \vdash \phi_1 \land \phi_2 }{ \Gamma \vdash \phi_1 }\) | \(\inferrule{} { \Gamma \vdash e : (\phi_1, \phi_2) }{ \Gamma \vdash \text{case } e \text{ of } (x_1, x_2) \rightarrow x_1 : \phi_1 }\) |
Right Elimination | \(\inferrule{} { \Gamma \vdash \phi_1 \land \phi_2 }{ \Gamma \vdash \phi_2 }\) | \(\inferrule{} { \Gamma \vdash e : (\phi_1, \phi_2) }{ \Gamma \vdash \text{case } e \text{ of } (x_1, x_2) \rightarrow x_2 : \phi_2 }\) |
Introduction | \(\inferrule{} { \Gamma \vdash \phi_1 \\ \Gamma \vdash \phi_2 }{ \Gamma \vdash \phi_1 \land \phi_2 }\) | \(\inferrule{} { \Gamma \vdash \text{case } e \text{ of } (x_1, x_2) \rightarrow x_1 : \phi_1 \\ \Gamma \vdash \text{case } e \text{ of } (x_1, x_2) \rightarrow x_2 : \phi_2 }{ \Gamma \vdash e : (\phi_1, \phi_2) }\) |
The type checking rules are not part of the core rules (that we saw in the first lecture), but they can be derived from the core rules.
If we ignore the expressions in the type checking rules, we take exactly the conjuction rules of natural deduction.
You can generate Haskell terms that provide evidence for the natural deduction rules!
As an example, consider the logical formula:
\[ \textit{If } \phi_1 \textit{ and } \phi_2\land\phi_3, \text{ then } \phi_1\land \phi_3 .\]
The natural deduction devation tree is shown below:
\[ \inferrule {\land\text{-I}} { \phi_1,\phi_2\land\phi_3 \vdash \phi_1 \qquad \inferrule{\land\text{-R-E}} { \phi_1,\phi_2\land\phi_3 \vdash \phi_2\land\phi_3 }{ \phi_1,\phi_2\land\phi_3 \vdash \phi_3 } }{ \phi_1, \phi_2\land\phi_3 \vdash \phi_1\land \phi_3 } \]
Filling in the terms (and applying the correspondence of propositions and types) we get the type checking derivation tree:
\[ \inferrule {\land\text{-I}} { x_1:\phi_1,x_{23}:\phi_2\land\phi_3 \vdash x_1:\phi_1 \qquad \inferrule{\land\text{-R-E}} { x_1:\phi_1,x_{23}:\phi_2\land\phi_3 \vdash x_{23}: \phi_2\land\phi_3 }{ x_1:\phi_1,x_{23}:\phi_2\land\phi_3 \vdash \text{case } x_{23} \text{ of } (x_2, x_3) \rightarrow x_3: \phi_3 } }{ x_1:\phi_1,x_{23}: \phi_2\land\phi_3 \vdash (x_1, \text{case } x_{23} \text{ of } (x_2, x_3) \rightarrow x_3): \phi_1\land \phi_3 } \]
Let’s encode this proof in Liquid Haskell!
So, conjuction is encoded by the pair type.
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | \(\{v:() | e\}\) |
Conjunction | \(\phi_1 \land \phi_2\) | (\(\phi_1\), \(\phi_2\)) |
Disjunction | \(\phi_1 \lor \phi_2\) | |
Implication | \(\phi_1 \Rightarrow \phi_2\) | |
Negation | \(\lnot \phi\) | |
Forall | \(\forall x. \phi\) | |
Exists | \(\exists x. \phi\) |
Disjunction is encoded by the Either
type, which is a sum type that can be either Left
or Right
.
data Either a b = Left a | Right b -- defined in Prelude
If you know that \(\phi_1\) is true or \(\phi_2\) is true, then Left ()
is a term that shows that \(\phi_1 \lor \phi_2\) is true and Right ()
is a term that shows that \(\phi_1 \lor \phi_2\) is true.
Deduction Rules: Dually to conjunction, disjunction has three rules: left and right introduction and elimination.
Rule | Natural Deduction | Type Checking |
---|---|---|
Left Introduction | \(\inferrule{}{ \Gamma \vdash \phi_1}{\Gamma \vdash \phi_1 \lor \phi_2}\) | \(\inferrule{} { \Gamma \vdash e : \phi_1 }{ \Gamma \vdash \text{Left } e : \text{Either } \phi_1 \ \phi_2 }\) |
Right Introduction | \(\inferrule{}{ \Gamma \vdash \phi_2}{\Gamma \vdash \phi_1 \lor \phi_2}\) | \(\inferrule{} { \Gamma \vdash e : \phi_2 }{ \Gamma \vdash \text{Right } e : \text{Either } \phi_1 \ \phi_2 }\) |
Elimination | \(\inferrule{} { \Gamma \vdash \phi_1 \lor \phi_2 \\ \Gamma, \phi_1 \vdash \phi \\ \Gamma, \phi_2 \vdash \phi }{ \Gamma \vdash \phi }\) | \(\inferrule{} { \Gamma \vdash e : \text{Either } \phi_1\ \phi_2 \\ \Gamma, x_1 : \phi_1 \vdash e_1 : \phi \\ \Gamma, x_2 : \phi_2 \vdash e_2 : \phi }{ \Gamma \vdash \text{case } e \text{ of } \{\text{Left } x_1 \rightarrow x_1 ; \text{Right } x_2 \rightarrow x_2 \} : \phi }\) |
So, disjunction is encoded by the either type.
Consider the valid logical formula: \[ \textit{If } \phi \lor \text{ false,} \textit{ then } \phi .\]
In Liquid Haskell, it is encoded usint the below type specification:
Question: Can you prove this property in Liquid Haskell?
The function exOr
can be defined as follows:
exOr ϕ (Left p) = p
exOr ϕ (Right p) = error "impossible"
So, disjunction is encoded by the either type:
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | \(\{v:() | e\}\) |
Conjunction | \(\phi_1 \land \phi_2\) | (\(\phi_1\), \(\phi_2\)) |
Disjunction | \(\phi_1 \lor \phi_2\) | Either \(\phi_1\) \(\phi_2\) |
Implication | \(\phi_1 \Rightarrow \phi_2\) | |
Negation | \(\lnot \phi\) | |
Forall | \(\forall x. \phi\) | |
Exists | \(\exists x. \phi\) |
Implication is encoded by the function type. It has two rules: introduction and elimination:
Rule | Natural Deduction | Type Checking |
---|---|---|
Introduction | \(\inferrule{}{ \Gamma, \phi_x \vdash \phi}{\Gamma \vdash \phi_x \Rightarrow \phi}\) | \(\inferrule{} { \Gamma, x : \phi_x \vdash e : \phi }{ \Gamma \vdash \lambda x. e : \phi_x \rightarrow \phi }\) |
Elimination | \(\inferrule{} { \Gamma \vdash \phi_x \\ \Gamma \vdash \phi_x \Rightarrow \phi }{ \Gamma \vdash \phi }\) | \(\inferrule{} { \Gamma \vdash e_x : \phi_x \\ \Gamma \vdash e : (\phi_x \rightarrow \phi) }{ \Gamma \vdash e\ e_x : \phi }\) |
The Implication Elimination Rule for natural deduction is also known as the modus ponens rule.
Negation is just implication to False
.
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | \(\{v:() | e\}\) |
Conjunction | \(\phi_1 \land \phi_2\) | (\(\phi_1\), \(\phi_2\)) |
Disjunction | \(\phi_1 \lor \phi_2\) | Either \(\phi_1\) \(\phi_2\) |
Implication | \(\phi_1 \Rightarrow \phi_2\) | \(\phi_1 \rightarrow \phi_2\) |
Negation | \(\lnot \phi\) | \(\phi \rightarrow \{\text{false}\}\) |
Forall | \(\forall x. \phi\) | |
Exists | \(\exists x. \phi\) |
Consider the valid logical formula: \[ \phi_C \doteq \lnot \phi \land \phi \Rightarrow \text{false}\]
In Liquid Haskell, it is encoded usint the below type specification:Question: Can you prove this property in Liquid Haskell?
The function exC
can be defined as follows:
exC ϕ (f, p) = f p
Consider the valid logical formula:
\[ \phi_{DN} \doteq \lnot \lnot \phi \Rightarrow \phi\]
In Liquid Haskell, it is encoded usint the below type specification:Question: Can you prove this property in Liquid Haskell?
The function exDN
can be defined as follows:
exDN ϕ nnp | not ϕ = nnp f
where
{-@ f :: {v:() | not ϕ && ϕ} -> {v:() | false} @-}
f :: () -> ()
f _ = ()
exDN ϕ nnp = ()
Universal quantification is encoded by the function type. It has two rules: introduction and elimination:
Rule | Natural Deduction | Type Checking |
---|---|---|
Introduction | \(\inferrule{}{ \Gamma \vdash \phi_x}{\Gamma \vdash \forall x. \phi_x}\) | \(\inferrule{} { \Gamma, x:\tau \vdash e : \phi_x }{ \Gamma \vdash \lambda x. e : x:\tau \rightarrow \phi_x }\) |
Elimination | \(\inferrule{} { \Gamma \vdash \forall x. \phi_x }{ \Gamma \vdash \phi_x [x / e_x] }\) | \(\inferrule{} { \Gamma \vdash e : (x:\tau \rightarrow \phi_x) \quad \Gamma \vdash e_x : \tau }{ \Gamma \vdash e\ e_x : \phi_x [x / e_x] }\) |
Existsential quantification is encoded by the dependent pair type. It has two rules: introduction and elimination:
Rule | Natural Deduction | Type Checking |
---|---|---|
Introduction | \(\inferrule{}{ \Gamma \vdash \phi_x[x / e_x]}{\Gamma \vdash \exists x. \phi_x}\) | \(\inferrule{} { \Gamma \vdash \text{fst } e : \tau \quad \Gamma, x:\tau \vdash \text{snd } e : \phi_x }{ \Gamma \vdash e : (x:\tau, \phi_x[x / \text{fst } e]) }\) |
Elimination | \(\inferrule{} { \Gamma \vdash \exists x. \phi_x \\ \Gamma, \phi_x[x / e_x] \vdash \phi }{ \Gamma \vdash : \phi }\) | \(\inferrule{} { \Gamma \vdash e : (x:\tau, \phi_x) \\ \Gamma, x:\tau, y:\phi_x \vdash e' : \phi }{ \Gamma \vdash \text{case } e \text{ of } (x, y) \rightarrow e' : \phi }\) |
These concludes the encoding of the rules of natural deduction in refinement types:
Logical Formula | Refinement Type | |
---|---|---|
Native Terms | \(e\) | \(\{v:() | e\}\) |
Conjunction | \(\phi_1 \land \phi_2\) | (\(\phi_1\), \(\phi_2\)) |
Disjunction | \(\phi_1 \lor \phi_2\) | Either \(\phi_1\) \(\phi_2\) |
Implication | \(\phi_1 \Rightarrow \phi_2\) | \(\phi_1 \rightarrow \phi_2\) |
Negation | \(\lnot \phi\) | \(\phi \rightarrow \{\text{false}\}\) |
Forall | \(\forall x. \phi\) | \(x:\tau \rightarrow \phi\) |
Exists | \(\exists x. \phi\) | \((x:\tau, \phi)\) |
Let’s now see some examples that use it!
This example encodes a proof that if there exists an \(x\) that satisfies a property forall \(y\), then forall \(y\) there exists an \(x\) that satisfies the property.
\[ \phi \doteq \exists x. \forall y. p\ x \ y \Rightarrow \forall y. \exists x. p \ x \ y \]
Let’s prove this propery in Liquid Haskell!
Question: Can you prove this property in Liquid Haskell?
The function exAll
can be defined as follows:
exAll p (x, f) y = (x, f y)
The natural deduction proof is shown below:
First, let’s distribute the exists quantifier over disjunction.
\[ \phi_\exists = (\exists x. p\ x \lor q\ x) \rightarrow ((\exists x. p\ x) \lor (\exists x. q\ x)) \]
Let’s prove this propery in Liquid Haskell!
Question: Can you prove this property in Liquid Haskell?
The function exDistOr
can be defined as follows:
{-@ exDistOr :: p:(a -> Bool) -> q:(a -> Bool)
-> (x::a, Either {v:() | p x} {v:() | q x})
-> Either (x::a, {v:() | p x}) (x::a, {v:() | q x}) @-}
exDistOr :: (a -> Bool) -> (a -> Bool)
-> (a, Either () ())
-> Either (a,()) (a,())
exDistOr p q (x, Left f) = Left (x, f )
exDistOr p q (x, Right g) = Right (x, g )
Now, let’s distribute the forall quantifier over conjunction.
\[ \phi_\forall = (\forall x. p\ x \land q\ x) \rightarrow ((\forall x. p\ x) \land (\forall x. q\ x))\]
Let’s prove this propery in Liquid Haskell!
Question: Can you prove this property in Liquid Haskell?
The function allDistAnd
can be defined as follows:
{-@ allDistAnd :: p:(a -> Bool) -> q:(a -> Bool)
-> (x:a -> ({v:() | p x}, {v:() | q x}))
-> (x:a -> {v:() | p x}, x:a -> {v:() | q x}) @-}
allDistAnd :: (a -> Bool) -> (a -> Bool)
-> (a -> ((), ()))
-> (a -> (), a -> ())
allDistAnd p q xpq = (\x -> case xpq x of (p,_) -> p,
\x -> case xpq x of (_,q) -> q)
Next, let’s prove that for all lists that are constructed by appending a list to itself, there exists an integer that is half the length of the list.
\[ \phi = \forall xs. (\exists ys. xs = ys \text{ ++ } ys) \rightarrow (\exists n. \text{length } xs = n + n )\]
Let’s prove this propery in Liquid Haskell!
Question: Can you prove the above property in Liquid Haskell? Hint: You can use the property lenAppend
:
The function evenLen
can be defined as follows:
{-@ evenLen :: xs:[a] -> (ys::[a], {v:() | xs == ys ++ ys})
-> (n::{v:Int | 0 <= v}, {v:() | len xs == n + n}) @-}
evenLen :: [a] -> ([a], ()) -> (Int, ())
evenLen xs (ys, ()) = (length ys, lenAppend ys ys)
Finally, let’s prove that for all natural numbers,
\[ \phi = (p \ 0 \land (\forall n. p \ (n-1) \rightarrow p \ n))) \rightarrow \forall n. p \ n\]
Let’s prove this propery in Liquid Haskell!
The function natInd
can be defined as follows:
{-@ natInd :: p:({v:Int | 0 <= v} -> Bool)
-> ({v:() | p 0}, n:{v:Int | 0 <= v} -> {v:() | p (n-1)} -> {v:() | p n})
-> n:{v:Int | 0 <= v} -> {v:() | p n} / [n] @-}
natInd :: (Int -> Bool) -> ((), Int -> () -> ()) -> Int -> ()
natInd p (p0, pn) n
= if n == 0 then p0 else pn n (natInd p (p0, pn) (n-1))
In this lecture, we saw that natural deduction can be encoded with refinement types, and specifically in Liquid Haskell. We saw that the rules of natural deduction can be encoded by the type checking rules of Liquid Haskell, where the expressions provide evidence for the rules. We saw that the Curry-Howard correspondence can be extended to higher order logic. We saw that we can prove higher order properties in Liquid Haskell.