Proving Predicate Logic using Coq - Beginner Syntax - predicate

I'm trying to prove the following in Coq:
Goal (forall x:X, P(x) /\ Q(x)) -> ((forall x:X, P (x)) /\ (forall x:X, Q (x))).
Can someone please help? I'm not sure whether to split, make an assumption etc.
My apologies for being a complete noob

Goal forall (X : Type) (P Q : X->Prop),
(forall x : X, P x /\ Q x) -> (forall x : X, P x) /\ (forall x : X, Q x).
Proof.
intros X P Q H; split; intro x; apply (H x).
Qed.

Just some hints:
I recommand you use intros to name your hypothesis, split to separate the goals,
and exact to provide the proof terms (which may involve proj1 or proj2).

Related

How to do cases on a function between finite types?

I'm just starting out with lean.
Say I want to prove something about functions between two finite types. For example
example (f : Bool -> Bool) : (∀ x : Bool, f (f (f x)) = f x) := sorry
since there are just a few possibilities for f ideally I'd want something like cases x <;> cases f <;> rfl, but I can't do cases f.
Currently, I don't know how to prove this at all, I'd think cases/match on (f false) and (f true) but lean doesn't seem to remember this information about the function once it goes into the case.
Here's a proof along your original lines (not dec_trivial), note we use the h : syntax to name the case hypothesis:
example (f : Bool -> Bool) : (∀ x : Bool, f (f (f x)) = f x) :=
by
intro x
cases x <;>
cases h : f true <;>
cases h2 : f false <;>
simp [h, h2]

How to turn Integer-List Generator [m ...] in Lambda Calculus in Haskell

I have given a task to make the Integer-List Generator [m...] in lambda calculus.
So it should fullfill this definition.
Y F m ≡ : m (Y F (+ m 1))
Therefor a lambda calculus F is needed.
I don't know how to find which lambda calculus F should be.
Does anyone have any suggestions for F?
Equational reasoning gets you where you need to go. We have these two equations:
Y F = F (Y F) -- the basic useful property of Y
Y F m = : m (Y F (+ m 1))
So now we just solve. We replace the Y F in Y F m = ... with the thing it's equal to:
F (Y F) m = : m (Y F (+ m 1))
One solution to this equation is to generalize from Y F to an arbitrary variable g everywhere:
F g m = : m (g (+ m 1))
Done. This is now a fine defining equation for F. If you don't like the syntax sugar, you could instead write it as a lambda:
F = \g m -> : m (g (+ m 1))
Of course, when you practice this yourself on other problems, be kind to yourself: there are lots of different choices of how to rewrite things at each step, and you might have to play with a couple different ways before you stumble on one that gets you where you want to go, rather than following such a straight-line path as I've outlined here where I had already tried and eliminated a bunch of wrong ways. Persevere, and you can learn to do it.
I would like to provide some notes on the relationship of [m..] and y f m = m : (y f (+ m 1)). Starting with the observation
[m..] = m : [m+1..]
and replacing [m..] using g m = [m..], we arrive at
g m = m : (g (m+1))
Taken as a definition of g, observe that this is a recursive definition. Recursive definitions can be split into a generic recursive part y (given by y f = f (y f)) and a non-recursive part f
g = y f
This replaces the recursive g by the non-recursive f which we still have to determine/solve for. Substituting g in the previous equation gives
y f m = m : (y f (m+1))
The take away message is that a recursive function (g) can be refactored into a non-recursive function (f). All you need is a single recursive function (y) which you can reuse for other recursive functions/problems.

Nested pattern matching in Lean for destructing hypothesis

Let us look at the example of some lemma (whose statement and whether it is true or not is irrelevant for this discussion):
lemma L1 : forall (n m: ℕ) (p : ℕ → Prop), (p n ∧ ∃ (u:ℕ), p u ∧ p m) ∨ (¬p n ∧ p m) → n = m :=
begin
intros n m p H, cases H with H H,
{cases H with H1 H2, cases H2 with u H2, cases H2 with H2 H3, sorry},
{cases H with H1 H2, sorry}
end
The point I wish to highlight here is when destructing my hypothesis with the cases tactic,
I did not know any other way but to use the tactic several times (once for each 'layer' so to speak).
If I look at the same lemma in Coq:
Lemma L1 : forall (n m:nat) (p:nat -> Prop),
(p n /\ exists (u:nat), p u /\ p m) \/ (~p n /\ p m) -> n = m.
Proof.
intros n m p [[H1 [u [H2 H3]]]|[H1 H2]].
- admit.
-
Show.
I am able to destruct my assumption with a single nested pattern match.
I am guessing I can do the same sort of thing in Lean but I do not know how. I would be grateful to be told as I find the nested pattern match very convenient in practice.
You'll need mathlib for this, and import tactic.rcases. You can use the rcases tactic.
import tactic.rcases
lemma L1 : forall (n m: ℕ) (p : ℕ → Prop), (p n ∧ ∃ (u:ℕ), p u ∧ p m) ∨ (¬p n ∧ p m) → n = m :=
begin
intros n m p H,
rcases H with ⟨H1, u, H2, H3⟩ | ⟨H1, H2⟩,
end

How to prove this lemma about eta expansion?

(here is a gist of my work so far.)
Coq comes with a rule about eta reduction, allowing us to prove something like the following:
Variables X Y Z : Type.
Variable f : X -> Y -> Z.
Lemma eta1 : (fun x => f x) = f.
Proof.
auto.
Qed.
The eta rule is not merely an equality rewrite, so we can use it beneath binders as well:
Lemma eta2 : (fun x y => f x y) = f.
Proof.
auto.
Qed.
Of course, one would expect that you could generalize this to an f of arbitrary arity. Here is my naïve attempt:
Require Import Coq.Lists.List.
Import ListNotations.
Fixpoint toType(ts : list Type)(t : Type) : Type :=
match ts with
|[] => t
|u::vs => u -> (toType vs t)
end.
Compute toType [X;Y] Z.
(*
= X -> Y -> Z
: Type
*)
Fixpoint etaexpand(ts : list Type) : forall t : Type, toType ts t -> toType ts t :=
match ts as ts return forall t, toType ts t -> toType ts t with
|[] => fun t x => x
|u::vs => fun t f (x:u) => etaexpand vs t (f x)
end.
Compute etaexpand [X;Y] Z f.
(*
= fun (x : X) (x0 : Y) => f x x0
: toType [X; Y] Z
*)
Lemma etaexpand_id : forall ts t f, etaexpand ts t f = f.
Proof.
induction ts; intros.
auto.
simpl.
(*stuck here*)
I get stuck on the inductive step of the above lemma. Naturally, I want to rewrite using the inductive hypothesis, but I cannot since the relevant subterm occurs beneath a binder. Of course, I myself know that the inductive hypothesis should be usable beneath binders, since it's just a chain of eta rewrites. I'm wondering then if there's a clever way to state and convince Coq of this fact.
In case anyone's curious, here's the solution I came up with after some thought.
The key is to simultaneously prove the following "niceness" property for etaexpand ts t:
Definition nice{X Y}(F : Y -> Y) := (forall y, F y = y) -> forall f : X -> Y,
(fun x => F (f x)) = f.

Y Combinator in Haskell

Is it possible to write the Y Combinator in Haskell?
It seems like it would have an infinitely recursive type.
Y :: f -> b -> c
where f :: (f -> b -> c)
or something. Even a simple slightly factored factorial
factMaker _ 0 = 1
factMaker fn n = n * ((fn fn) (n -1)
{- to be called as
(factMaker factMaker) 5
-}
fails with "Occurs check: cannot construct the infinite type: t = t -> t2 -> t1"
(The Y combinator looks like this
(define Y
(lambda (X)
((lambda (procedure)
(X (lambda (arg) ((procedure procedure) arg))))
(lambda (procedure)
(X (lambda (arg) ((procedure procedure) arg)))))))
in scheme)
Or, more succinctly as
(λ (f) ((λ (x) (f (λ (a) ((x x) a))))
(λ (x) (f (λ (a) ((x x) a))))))
For the applicative order
And
(λ (f) ((λ (x) (f (x x)))
(λ (x) (f (x x)))))
Which is just a eta contraction away for the lazy version.
If you prefer short variable names.
Here's a non-recursive definition of the y-combinator in haskell:
newtype Mu a = Mu (Mu a -> a)
y f = (\h -> h $ Mu h) (\x -> f . (\(Mu g) -> g) x $ x)
hat tip
The Y combinator can't be typed using Hindley-Milner types, the polymorphic lambda calculus on which Haskell's type system is based. You can prove this by appeal to the rules of the type system.
I don't know if it's possible to type the Y combinator by giving it a higher-rank type. It would surprise me, but I don't have a proof that it's not possible. (The key would be to identify a suitably polymorphic type for the lambda-bound x.)
If you want a fixed-point operator in Haskell, you can define one very easily because in Haskell, let-binding has fixed-point semantics:
fix :: (a -> a) -> a
fix f = f (fix f)
You can use this in the usual way to define functions and even some finite or infinite data structures.
It is also possible to use functions on recursive types to implement fixed points.
If you're interested in programming with fixed points, you want to read Bruce McAdam's technical report That About Wraps it Up.
The canonical definition of the Y combinator is as follows:
y = \f -> (\x -> f (x x)) (\x -> f (x x))
But it doesn't type check in Haskell because of the x x, since it would require an infinite type:
x :: a -> b -- x is a function
x :: a -- x is applied to x
--------------------------------
a = a -> b -- infinite type
If the type system were to allow such recursive types, it would make type checking undecidable (prone to infinite loops).
But the Y combinator will work if you force it to typecheck, e.g. by using unsafeCoerce :: a -> b:
import Unsafe.Coerce
y :: (a -> a) -> a
y = \f -> (\x -> f (unsafeCoerce x x)) (\x -> f (unsafeCoerce x x))
main = putStrLn $ y ("circular reasoning works because " ++)
This is unsafe (obviously). rampion's answer demonstrates a safer way to write a fixpoint combinator in Haskell without using recursion.
Oh
this wiki page and
This Stack Overflow answer seem to answer my question.
I will write up more of an explanation later.
Now, I've found something interesting about that Mu type. Consider S = Mu Bool.
data S = S (S -> Bool)
If one treats S as a set and that equals sign as isomorphism, then the equation becomes
S ⇋ S -> Bool ⇋ Powerset(S)
So S is the set of sets that are isomorphic to their powerset!
But we know from Cantor's diagonal argument that the cardinality of Powerset(S) is always strictly greater than the cardinality of S, so they are never isomorphic.
I think this is why you can now define a fixed point operator, even though you can't without one.
Just to make rampion's code more readable:
-- Mu :: (Mu a -> a) -> Mu a
newtype Mu a = Mu (Mu a -> a)
w :: (Mu a -> a) -> a
w h = h (Mu h)
y :: (a -> a) -> a
y f = w (\(Mu x) -> f (w x))
-- y f = f . y f
in which w stands for the omega combinator w = \x -> x x, and y stands for the y combinator y = \f -> w . (f w).

Resources