| Copyright | (c) Matt Noonan 2018 |
|---|---|
| License | BSD-style |
| Maintainer | matt.noonan@gmail.com |
| Portability | portable |
| Safe Haskell | None |
| Language | Haskell2010 |
Logic.Proof
Contents
Description
- data Proof (pf :: *)
- (|.) :: (p -> Proof q) -> (q -> Proof r) -> p -> Proof r
- (|$) :: Proof p -> (p -> Proof q) -> Proof q
- (|/) :: (p -> (q -> Proof r) -> Proof r) -> (q -> Proof r) -> p -> Proof r
- (|\) :: (p -> Proof q) -> ((p -> Proof q) -> Proof r) -> Proof r
- ($$) :: (p -> q -> Proof r) -> Proof p -> q -> Proof r
- given :: p -> Proof p
- axiom :: Proof p
- sorry :: Proof p
The Proof monad
The Proof monad is used as a domain-specific
language for constructing proofs. A value of type
Proof p represents a proof of the proposition p.
For example, this function constructions a proof of "P or Q" from the assumption "P and Q":
and2or :: (p && q) -> Proof (p || q) and2or pq = do p <- and_elimL pq -- or: "p <- fst <$> and_elim pq" or_introL p
If the body of the proof does not match the proposition
you claim to be proving, the compiler will raise a type
error. Here, we accidentally try to use or_introR
instead of or_introL:
and2or :: (p && q) -> Proof (p || q) and2or pq = do p <- and_elimL pq or_introR p
resulting in the error
• Couldn't match type ‘p’ with ‘q’
‘p’ is a rigid type variable bound by
the type signature for:
and2or :: forall p q. (p && q) -> Proof (p || q)
‘q’ is a rigid type variable bound by
the type signature for:
and2or :: forall p q. (p && q) -> Proof (p || q)
Expected type: Proof (p || q)
Actual type: Proof (p || p)
(|.) :: (p -> Proof q) -> (q -> Proof r) -> p -> Proof r infixr 9 Source #
This operator is used to create nicely delineated chains of
derivations within a larger proof. If X and Y are two
deduction rules, each with a single premise and a single
conclusion, and the premise of Y matches the conclusion of X,
then X |. Y represents the composition of the two
deduction rules.
and2or :: (p && q) -> Proof (p || q)
and2or = and_elimL
|. or_introL(|$) :: Proof p -> (p -> Proof q) -> Proof q infixr 7 Source #
This operator is just a specialization of (>>=), but
can be used to create nicely delineated chains of
derivations within a larger proof. The first statement
in the chain should produce a formula; (|$) then
pipes this formula into the following derivation rule.
and2or :: (p && q) -> Proof (p || q)
and2or pq = and_elimL pq
|$ or_introL(|/) :: (p -> (q -> Proof r) -> Proof r) -> (q -> Proof r) -> p -> Proof r infixr 9 Source #
The (|/) operator is used to feed the remainder of the proof in
as a premise of the first argument.
A common use-case is with the Or-elimination rules or_elimL and
or_elimR, when one case is trivial. For example, suppose we wanted
to prove that R && (P or (Q and (Q implies P))) implies P:
my_proof :: r && (p || (q && (q --> p))) -> Proof p
my_proof =
do and_elimR -- Forget the irrelevant r.
|. or_elimL given -- The first case of the || is immediate;
|/ and_elim -- The rest of the proof handles the second case,
|. uncurry impl_elim -- by unpacking the && and feeding the q into
-- the implication (q --> p).
The rising / is meant to suggest the bottom half of the proof getting
plugged in to the Or-elimination line.
(|\) :: (p -> Proof q) -> ((p -> Proof q) -> Proof r) -> Proof r infixl 8 Source #
The (|\) operator is used to take the subproof so far and feed it
into a rule that is expecting a subproof as a premise.
A common use-case is with the Not-introduction rule not_intro,
which has a type that fits the second argument of (|\). By way
of example, here is a proof that "P implies Q" along with "Not Q"
implies "Not P".
my_proof :: (p --> q) -> (Not p --> r) -> Not q -> Proof r my_proof impl1 impl2 q' = do modus_ponens impl1 -- This line, composed with the next, |. contradicts' q' -- form a proof of FALSE from p. |\ not_intro -- Closing the subproof above, conclude not-p. |. modus_ponens impl2 -- Now apply the second implication to conclude r.
The falling \ is meant to suggest the upper half of the proof
being closed off by the Not-introduction line.
($$) :: (p -> q -> Proof r) -> Proof p -> q -> Proof r Source #
Take a proof of p and feed it in as the first premise of
an argument that expects a p and a q.
given :: p -> Proof p Source #
given creates a proof of P, given P as
an assumption.
given is just a specialization of pure / return.
axiom, like sorry, provides a "proof" of any
proposition. Unlike sorry, which is used to indicate
that a proof is still in progress, axiom is meant to
be used by library authors to assert axioms about how
their library works. For example:
data Reverse xs = Reverse Defn data Length xs = Length Defn rev_length_lemma :: Proof (Length (Reverse xs) == Length xs) rev_length_lemma = axiom